#include "stdafx.h"
#include "CScrLanguage.h"
#include "CScripts.h"
#include "CTokens.h"
#include "CMacros.h"
#include "CConsole.h"
#include "CParser.h"

CParser Parser;

eTokenType				CParser::s_eTokenType;
eTokenType				CParser::s_ePredictedNextType;
eTokenType				CParser::s_eCastRealTokenType;
CScriptFile			*	CParser::s_pScriptFile;
CScript				*	CParser::s_pScript = nullptr;
CSubScript			*	CParser::s_pSubScript = nullptr;
const char			*	CParser::s_pTypeName = nullptr;
const CCommand		*	CParser::s_pCommand = nullptr;
CCommandArg			*	CParser::s_pCommandArg = nullptr;
CVariable			*	CParser::s_pVariable = nullptr;
COperator			*	CParser::s_pOperator = nullptr;
CLabel				*	CParser::s_pLabel = nullptr;
CLabel				*	CParser::s_pFunctionLabel;
CLabel				*	CParser::s_pFunctionEndLabel;
COperation 			*	CParser::s_pOperations[];
CControlStruct		*	CParser::s_pControlStructure = nullptr;
CMacro				*	CParser::s_pMacro = nullptr;
CMacro				*	CParser::s_pDeclaredMacro = nullptr;
CMacroParam			*	CParser::s_pMacroParam = nullptr;
int						CLimits::s_nJumpTableSize;
int						CParser::s_nConstructID;
int						CParser::s_nNumLinesParsed = 0;
int						CParser::s_nNumCodeLines = 0;
int						CParser::s_nNumTokens = 0;
int						CParser::s_nNumRegisteredTokens = 0;
int						CParser::s_nNumAsmStructs;
int						CParser::s_nNumFunctions;
int						CParser::s_nPushedRegisteredTokens;
int						CParser::s_nPushedLinesParsed;
int						CParser::s_nNumInlineOperations = 0;
int						CParser::s_nNumStringLiteralChars = 0;
const CTypeBase		*	CParser::s_pDeclaredVarType;		// (basic type)
const CTypeBase		*	CParser::s_pCastedMagicType;
int						CParser::s_nHexBeginLine;
int						CParser::s_nAsmBeginLine;
int						CParser::s_nAsmStructID;
char					CParser::s_pLineBuffer[];
char				*	CParser::s_pLineBegin;
char				*	CParser::s_pLineCurrent;
char				*	CParser::s_pLineEnd;
char				*	CParser::s_pLineTokens[];
char					CParser::s_szIdentifier[];
char					CParser::s_szStringLiteral[];
const char			*	CParser::s_pCurrentToken;
CToken				*	CParser::s_pToken;
CToken				*	CParser::s_pLogicToken;
CToken				*	CParser::s_pNullToken = nullptr;
CToken				*	CParser::s_pLastToken;
CToken				*	CParser::s_pEOALToken;
CToken				*	CParser::s_pTokens[][IDENTIFIERS_PER_LINE];
bool					CParser::s_bInConsoleMode;
bool					CParser::s_bLabelIsDefined;
bool					CParser::s_bLineContinuesConditions;
bool					CParser::s_bCommentActive;
bool					CParser::s_bStringActive;
bool					CParser::s_bJustOutOfString;
bool					CParser::s_bIsInControlStruct;
bool					CParser::s_bIsInConstStruct;
bool					CParser::s_bIsInHexStruct;
bool					CParser::s_bIsInAsmStruct;
bool					CParser::s_bInlinePreprocessing;
bool					CParser::s_bInPreprocessorControlStruct;
bool					CParser::s_bIsInLoopingPreprocessorBlock;
bool					CParser::s_bLineHasLabel = false;
bool					CParser::s_bLogicalOperatorNot = false;
bool					CParser::s_bIsDeclaringMacro = false;
bool					CParser::s_bDontFeedFromMacro = false;
bool					CParser::s_bWeAreBeingFedByMacro = false;
bool					CParser::s_bMacroIsFunctiony;
bool					CParser::s_bIsInPreprocessorDirective;
bool					CParser::s_bParseOnlyMode;
bool					CParser::s_bExpectAnything;
bool					CParser::s_bNoPredefined;
bool					CParser::s_bIsInHeader;
bool					CParser::s_bCastMagicActivated;
bool					CParser::s_bErrorHandled;
int						CParser::s_bLogicalOperator = -1;
int						CParser::s_nLastParseID;
int						CParser::s_nNumIFs;
int						CParser::s_nNumREPEATs;
int						CParser::s_nNumWHILEs;
int						CParser::s_nNumConditions;
int						CParser::s_nMacroTokenIdx;
int						CParser::s_nControlStructDepth;
int						CParser::s_nNumComments;
int						CParser::s_nNumSwitchCases;
int						CParser::s_nSwitchDepth;
int						CParser::s_nNumMacroParamsPassed;
int						CParser::s_nBaseVarTokenIdx;
int						CParser::s_nPreprocessorLoopStart;
DWORD					CParser::s_dwNumBytes;

int						CParser::s_bPreprocessorLogicHistory[] = {1};
int						CParser::s_nPreprocessorControlDepth;
CParser::sRegister		CParser::s_PreprocessorRegisters[];

CParser::CCastHistory	CParser::s_CastHistory[64];
CParser::CCastHistory *	CParser::s_pCast;

CParser::sAsmStruct	CParser::s_AsmStructs[];
CParser::sAsmStruct * CParser::s_pAsmStruct;

CList<CToken>			CParser::s_MaybeLabelTokens;

void CParser::Parse(CScriptFile * pScriptFile)
{
	s_pScriptFile = pScriptFile;

	// We do most of the work!
	try
	{
		const char * szProgMsg = SCRambl.GetText("GENERAL", "M106");
		while(ReadLine(pScriptFile))
		{
			short prog = (short)floor(((float)pScriptFile->GetPos() / (float)pScriptFile->GetSize()) * 100.0); 
			char * text = SCRambl.BuildText(szProgMsg, NULL, 0, "%s%s", pScriptFile->GetName(), tostr(prog));
			Output(text);
			Output("\r");
			FlushOutput();
			PreparseLine();
		}

		Output("\n");

		// Check for things that shouldn't be (these dont abort compilation, however)
		if(s_nPreprocessorControlDepth)
			Error(SCRambl.GetText("ERRORS", "E1019"));
		if(s_bCommentActive)
			Error(SCRambl.GetText("ERRORS", "E1020"));
		if(s_bStringActive)
			Error(SCRambl.GetText("ERRORS", "E1021"));
		if(s_bIsInControlStruct)
			Error(SCRambl.GetText("ERRORS", "E1022"));
		if(s_bIsInHexStruct)
			Error(SCRambl.GetText("ERRORS", "E1023"));
		if(s_bIsInAsmStruct)
			Error(SCRambl.GetText("ERRORS", "E1024"));
		if(s_nLastParseID == SCRIPT_IF)
			Error(SCRambl.GetText("ERRORS", "E1025"));

		Print(SCRambl.GetText("GENERAL", "DONE"));
	}
	catch(const char *e)
	{
		Error(e);
		throw(SCRambl.BuildText(SCRambl.GetText("PARSER", "M100"), NULL, 0, "%s", pScriptFile->GetName()));
	}
}
int CParser::Parse(CSubScript * pSubScript)
{
	s_pSubScript = pSubScript;
	s_pScript = pSubScript->GetScript();
	
	// Initialize parsing engine
	Init();

	try
	{
		Print("");
		BuildEvents[PARSE_SUBSCRIPT][BEFORE]();
		Parse(pSubScript->GetFile());

		// Assembl
		AssembleASM();

		// Correct label offsets
		CalculateOffsets();

		// Delete temporary files
		//DeleteFile("temp.asm");
		BuildEvents[PARSE_SUBSCRIPT][AFTER]();

		// Reset push'd config
		SCRambl.Config.Parser.ResetConfig();
	}
	catch(const char *e)
	{
		// Handle an error in preparsing, which is most likely a code error
		s_dwNumBytes = -1;
		Print("");
		//if(errno)
			//Exception(strerror(errno));
		Print(e);
	}

	// Create list file:
	if(s_dwNumBytes == -1)
	{
		CreateListFile();
		s_dwNumBytes = -1;
	}
	else CreateListFile();

	// Shut down the parsing engine
	Shutdown();
	Print("");
	return s_dwNumBytes;
}

void CParser::CreateListFile()
{
	// Save token info to a file
	if(!SCRambl.Config.Parser.CreateListFile()) return;
	char szFileName[MAX_PATH], * pStr;
	
	Print(SCRambl.GetText("PARSER", "M109"));
	strcpy(szFileName, s_pScriptFile->GetName());
	pStr = strrchr(szFileName, '\\');
	pStr = pStr ? ++pStr : szFileName;
	*strrchr(pStr, '.') = '\0';
	strcat(pStr, ".lst");

	if(auto pFile = new CScriptFile(pStr, FILE_MODE_WRITE, FILE_TYPE_TEXT))
	{
		if(pFile->OK())
		{
			pFile->WriteLine(SCRambl.BuildText(SCRambl.GetText("PARSER", "M101"), NULL, 0, "%s", g_pScript->GetFile()->GetName()));

			for(int x=0; x < s_nNumLinesParsed; x++)
			{
				CVariable * pVar;
				pFile->WriteLineFormat("\n%d: [%d] ", x, s_dwLineOffsets[x]);
				for(int y=0; y < MAX_TOKENS; y++)
				{
					auto pToken = GetToken(x, y);

					if(!pToken) break;

					int i;

					switch(pToken->GetType())
					{
					case TOKEN_TYPENAME:
						pFile->WriteLineFormat("%s", CSyntax::GetType(pToken->GetIntValue())->GetName());
						break;
					case TOKEN_VAR:
						for(i=0; pToken; ++i)
						{
							pVar = pToken->GetVariableValue();
							pFile->WriteLineFormat("%s(%d%c)", pVar->GetName(), pVar->GetIndex(), pVar->IsLocal() ? 'l' : 'v');
							if(pToken = pToken->GetSubToken())
								pFile->WriteLine("[");
						}
						while(--i) pFile->WriteLine("]");
						break;
					case TOKEN_VARINDEX:
						pFile->WriteLineFormat("%s(%d)", pToken->GetString(), pToken->GetIntValue());
						break;
					case TOKEN_LABEL:
						pFile->WriteLineFormat(pToken->IsSkippedDuringCompilation() ? "%s:" : "%s", pToken->GetLabelValue()->GetName());
						break;
					case TOKEN_OPERATOR:
						pFile->WriteLineFormat("%04X: %s",	pToken->GetOperationValue()->GetId(),
														pToken->GetOperationValue()->GetOperator()->GetOperatorString());
						break;
					case TOKEN_COMMAND:
						pFile->WriteLineFormat("%04X: %s", pToken->GetOperationValue()->GetId(),
													pToken->GetCommandValue()->GetName());
						break;
					case TOKEN_VALUE:
						pFile->WriteLineFormat("%s", pToken->GetString());
						break;
					case TOKEN_STRING:
						pFile->WriteLineFormat("%s", pToken->GetString());
						break;
					case TOKEN_HEX:
						pFile->WriteLineFormat("HEX(%s)", pToken->GetString());
						break;
					case TOKEN_HEX_BYTE:
					case TOKEN_HEX_WORD:
					case TOKEN_HEX_DWORD:
					case TOKEN_HEXMAYBELABEL:
						pFile->WriteLineFormat("HEX(%X)", pToken->GetIntValue());
						break;
					case TOKEN_CONSTRUCT:
						pFile->WriteLineFormat("%s", CSyntax::GetConstructName(pToken->GetConstruct()));
						break;
					case TOKEN_MACRO:
						pFile->WriteLineFormat("MACRO(%s)", pToken->GetMacroValue()->GetName());
						break;
					case TOKEN_VARARG_NUM_ARGS:
						pFile->WriteLineFormat("VARARG(%d)", pToken->GetIntValue());
					default:
						pFile->WriteLine(pToken->GetString());
						break;
					}
					pFile->WriteLine(" ");
				}
			}
		}
		else Print(SCRambl.GetText("PARSER", "M110"));
		delete pFile;
	}
}
void CParser::AssembleASM()
{
	// Add ASM structure contents to a file to compile in NASM and compile with the script later
	CToken		*	pToken,
				*	pLabelRefs[MAX_ASM_LABEL_PTRS],
				*	pExtLabelPtrs[MAX_ASM_LABEL_PTRS];
	int				nNumLabelPtrs;
	int				nNumExtLabelPtrs;
	
	// Iterate through non-assembled ASM structs
	for(int i=0; i<s_nNumAsmStructs; i++)
	{
		if(s_AsmStructs[i].m_bIsAssembled)
			continue;

		// Create a temporary ASM file to send to the assembler
		if(FILE * hAsmFile = OpenFile("temp.asm", "wt"))
		{
			char szTempString[128];
			FILE  *	nasm;
			sAsmStruct &pAsmStruct	= s_AsmStructs[i];
			int begin				= pAsmStruct.m_nStartLine,
				end					= pAsmStruct.m_nEndLine;
			nNumLabelPtrs		= 0;
			nNumExtLabelPtrs	= 0;

			// Loop through tokens in the ASM structures, write assembly code to file, add labels to a list
			for(int l=begin; l < end; l++)
			{
				for(int j=0; j < MAX_TOKENS; j++)
				{
					if(pToken = GetToken(l, j))
					{
						switch(pToken->GetType())
						{
						case TOKEN_ASM_LABEL_POINTER:
							pExtLabelPtrs[nNumExtLabelPtrs++] = pToken;
							WriteStringToFile(hAsmFile, "0xCDCDCDCD");
							break;
						case TOKEN_VAR:
							sprintf(g_szTempString, "0x%X", pToken->GetVariableValue()->GetIndex());
							WriteStringToFile(hAsmFile, g_szTempString);
							break;
						default:
							// Write the assembly code
							WriteStringToFile(hAsmFile, pToken->GetString());

							// A new label is declared on this line? Make a note of it...
							if(pToken->GetType() == TOKEN_LABEL)
							{
								pLabelRefs[nNumLabelPtrs++] = pToken;
								WriteStringToFile(hAsmFile, ":");
							}
							break;
						}
						WriteStringToFile(hAsmFile, " ");
					}
				}

				WriteStringToFile(hAsmFile, "\n");
			}

			// Compile offsets for label ptr's at end of code
			for(int j=0; j<nNumLabelPtrs; j++)
			{
				if(!pLabelRefs[j]) continue;

				// "JMP DWORD %labelname%" will assemble to an offset, e.g. "JMP 0x40FFFFFF"
				// We need to use all of these later to obtain the right offset for the label
				// when pointing to it from outside of the ASM structure (hacky)
				// Thanks to LINK2012 for the idea!
				WriteStringToFile(hAsmFile, "JMP DWORD ");
				WriteStringToFile(hAsmFile, pLabelRefs[j]->GetString());
				WriteStringToFile(hAsmFile, "\n");

				// Populate the ASM label list struct so we can quickly update the label offsets
				pAsmStruct.m_pLabelList[j] = pLabelRefs[j]->GetLabelValue();
			}

			CloseFile(hAsmFile);

			Trace("\n\n---SCRambl requires nasm.exe to assemble ASM structures---\n\n");

			// Run NASM
			// TODO: Integrate assemblies with projects - cache assemblies
			sprintf(szTempString, "asm%d.obj", i);
			sprintf(g_szTempString, "nasm.exe -fwin temp.asm -o%s -O%d -a -s", szTempString, SCRambl.Config.Optimisation.GetAsmMultipassLevel());
			if(!(nasm = OpenExecutable(g_szTempString)))
				throw(SCRambl.GetText("PARSER", "M102"));

			// Any errors are outputted to stdout, read any error and throw it
			if(!IsEndOfFile(nasm))
			{
				if(ReadStringFromFile(nasm, g_szTempString, sizeof(g_szTempString)))
					throw("%s", g_szTempString);
			}

			// Open the .obj file with the assembled code and copy it to a local buffer
			if(FILE *hFile = OpenFile(szTempString, "rb"))
			{
				// Skip the Win32 header, read the .text offset/size, read the raw code
				SeekFile(hFile, IMAGE_SIZEOF_FILE_HEADER + offsetof(IMAGE_SECTION_HEADER, SizeOfRawData), SEEK_SET);
				pAsmStruct.m_dwAssembledSize = ReadFromFile<DWORD>(hFile);
				DWORD dwOffset = FileGetCursorPos(hFile);
				
				// Go to .text
				SeekFile(hFile, ReadFromFile<DWORD>(hFile), SEEK_SET);
				DWORD dwOffset1 = FileGetCursorPos(hFile);

				// Though I fixed the bug where this happens because of SCRambl, the bug caused
				// huge amounts of memory usage. Better not risk it if the file is somehow corrupted..
				if(pAsmStruct.m_dwAssembledSize > 0x100000)
					throw(SCRambl.GetText("PARSER", "M104"));

				// Store code for later
				pAsmStruct.m_pData = new BYTE[pAsmStruct.m_dwAssembledSize];
				ReadBytesFromFile(hFile, pAsmStruct.m_pData, pAsmStruct.m_dwAssembledSize);
				CloseFile(hFile);
			}
			else throw(SCRambl.BuildText(SCRambl.GetText("PARSER", "M103"), NULL, 0, "%s", szTempString));

			// Store number of label pointers we need to calculate
			pAsmStruct.m_nNumLabels = nNumLabelPtrs;
			pAsmStruct.m_bIsAssembled = true;
		}
	}
}
void CParser::CalculateOffsets()
{
	CToken *pToken;

	// We will have to do this over and over, as changing the offsets changes the size of
	// data needed to point to them, which will change offsets ahead of the pointers...
	// Do it until repeating the process twice produces the same result.
	// This will usually only happen once. Twice if CASE statements are used.
	DWORD dwLastNumBytes;
	
	do
	{
		dwLastNumBytes = s_dwNumBytes;
		s_dwNumBytes = 0;
		
		for(int l=0; l <= s_nNumLinesParsed; l++)
		{
			s_dwLineOffsets[l] = s_dwNumBytes;

			for(int i=0; i < MAX_TOKENS; i++)
			{
				if(pToken = GetToken(l, i))
				{
					if(pToken->GetType() == TOKEN_ASM)
					{
						sAsmStruct &pAsmStruct = s_AsmStructs[pToken->GetIntValue()];

						// Get the beginning of the list of "JMP label"'s for the ASM struct
						sAsmLabelOffset * pLabelDef = (sAsmLabelOffset*)((BYTE*)pAsmStruct.m_pData +
							(pAsmStruct.m_dwAssembledSize - pAsmStruct.m_nNumLabels * sizeof(sAsmLabelOffset)));

						pAsmStruct.m_dwActualSize = PtrToInt(pLabelDef) - PtrToInt(pAsmStruct.m_pData);

						// Each script label has to be set to the correct ASM struct offset...
						for(int j=0; j < pAsmStruct.m_nNumLabels; j++)
						{
							// Get the ASM struct-relative offset
							DWORD dwOffset =	PtrToInt(pLabelDef) - PtrToInt(pAsmStruct.m_pData) +
												pLabelDef->val + sizeof(sAsmLabelOffset);

							// Use the struct offset to figure out the script offset
							pAsmStruct.m_pLabelList[j]->SetOffset(s_dwNumBytes + dwOffset);
							pLabelDef++;
						}

						// Movin' on now...
						s_dwNumBytes += pAsmStruct.m_dwActualSize;
						l = pAsmStruct.m_nEndLine;
						break;
					}

					if(pToken->IsSkippedDuringCompilation())
					{
						// Set label offset
						if(pToken->GetType() == TOKEN_LABEL && pToken->GetLabelValue())
							pToken->GetLabelValue()->SetOffset(s_dwNumBytes);
					}
					else
					{
						if(pToken->GetType() == TOKEN_HEXMAYBELABEL)
							throw(SCRambl.BuildText(SCRambl.GetText("PARSER", "M105"), NULL, 0, "%s%d", pToken->GetString(), pToken->GetIntValue()));
						else if(pToken->GetType() == TOKEN_LABEL)
						{
							if(auto pLabel = pToken->GetLabelValue())
							{
								if(pLabel->WaitingForDefinition())
									throw(SCRambl.BuildText(SCRambl.GetText("PARSER", "M106"), NULL, 0, "%s%d", pLabel->GetName(), pLabel->GetLineNum()));
							}
						}
						s_dwNumBytes += pToken->GetSize();
					}
				}
				else break;
			}
		}
	}
	while(s_dwNumBytes != dwLastNumBytes);
}
void CParser::PreparseLine()
{
	// This is where we will do the main process of converting a code line to
	// tokens in order to compile later. First we create tokens for code
	// identifiers as well as creating handles for the identifiers themselves
	// and then we use the token data from the current line to do any last
	// minute changes that may be necessary.

	// Clear any lingering tokens on the line
	ClearIdentifiers();
	ClearTokensOnLine(s_nNumLinesParsed);

	#if 0
	if((s_pLineEnd - s_pLineCurrent) > MAX_PARSE_LINE)
	{
		sprintf(g_szTempString, "Line exceeds limit of %d parsable characters", MAX_PARSE_LINE);
		CompileError(g_szTempString);
	}
	#endif

	// What wont be at the start of the line that may be by the end of the line?
	s_bIsInPreprocessorDirective = false;
	s_bLineContinuesConditions = false;
	s_bLogicalOperatorNot = false;
	s_bLineHasLabel = false;

	// Chop it, slice it, sort it, break it, fix, reload it...
	ProcessLine();

	// String still active? Continue on next line...
	if(s_bStringActive) return;

	CToken * pToken;

	do
	{
		// Get code data
		s_eTokenType = GetTokenData();

		// If we're in a false source control structure area, use a null token in case it ends on this line
		if(!s_bPreprocessorLogicHistory[s_nPreprocessorControlDepth] && s_eTokenType != TOKEN_DIRECTIVE)
			s_eTokenType = TOKEN_NONE;

		if(s_eTokenType != TOKEN_NONE)
		{
			// Where we going with this valuable token type information?
			bool bFeedMacro = s_bIsDeclaringMacro || (s_bWeAreBeingFedByMacro && s_bMacroIsFunctiony && s_nNumMacroParamsPassed < s_pMacro->GetNumParams());
			if(s_bIsInHexStruct && !s_bIsInPreprocessorDirective)
			{
				// The hex procedure will try to come up with a good use for some token types
				pToken = ProcessHexToken();
			}
			else if(bFeedMacro)
			{
				// We're in a macro definition, so do the bare minimum
				if(s_pCast->IsActive())
				{
					pToken = ProcessCast();
					AddToken(pToken);
				}
				else pToken = RegisterToken(s_eTokenType, s_pCurrentToken);

				// If it's another macro or a param, we'll need to save the pointers
				if(s_eTokenType == TOKEN_MACRO) pToken->SetValue(s_pMacro);
				else if(s_eTokenType == TOKEN_MACROPARAM) pToken->SetValue(s_pMacroParam);
			}
			else if(s_eTokenType == TOKEN_CONSTRUCT)
			{
				// NOT / AND / OR - set modifiers of the next command
				pToken = ProcessConstruct();
			}
			else
			{
				// We're in a pure SCR area and effectively at the start of the line, get the first script identifier/operand/token etc.
				s_nBaseVarTokenIdx = -1;

				// Does some invisible junk inbetween IF conditions and code need creating?
				if(s_eTokenType != TOKEN_CONSTRUCT)
				{
					if(s_nLastParseID == SCRIPT_IF && !s_bLineContinuesConditions)
						ProcessEndOfConditionList();
				}

				switch(s_eTokenType)
				{
				case TOKEN_VAR:
					// Cool, as long as there's an operator next...
					s_nBaseVarTokenIdx = s_nNumRegisteredTokens;
					pToken = ProcessVariable();
					if(s_bIsDeclaringMacro) break;
					s_eTokenType = GetTokenData();
					if(s_eTokenType != TOKEN_OPERATOR)
						throw(SCRambl.GetText("ERRORS", "E1026"));
				case TOKEN_OPERATOR:
					// Parse operator and operation args
					if(pToken = ProcessOperator())
					{
						if(!s_bIsDeclaringMacro) ProcessArgs(pToken);
					}
					break;
				case TOKEN_COMMAND:
					// Parse command and its args
					if(pToken = ProcessCommand())
					{
						if(!s_bIsDeclaringMacro) ProcessArgs(pToken);
					}
					break;
				case TOKEN_DIRECTIVE:
					// Directives identified by # prefix. Depending on the directive, we may move on in the line...
					ProcessDirective();
					break;

				
				case TOKEN_CONSTRUCT:
					// NOT / AND / OR - set modifiers of the next command
					pToken = ProcessConstruct();
					break;
				case TOKEN_TYPENAME:
					// SCR type name - declare variables of specific type
					ProcessTypeName();
					break;
				case TOKEN_LABEL:
					// Label: - declare label and add to the script
					pToken = ProcessLabel(true);
					break;
				case TOKEN_VALUE:
					throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1027"), NULL, 0, "%s", s_pCurrentToken));
					break;
				case TOKEN_STRING:
					if(s_bIsInAsmStruct)
					{
						pToken = RegisterToken(TOKEN_STRING, s_pCurrentToken);
                        pToken->SetValueType(PT_NONE);
					}
					else throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1028"), NULL, 0, "%s", s_pCurrentToken));
					break;
				case TOKEN_UNKNOWN:
				default:
					// Line totally confused us...
					throw("????????????");
					break;
				}
			}
		}

		/*if(s_bWeAreBeingFedByMacro)
			pToken = s_pToken;

		switch(s_eTokenType)
		{
		case TOKEN_MACRO:
			// Should only be here when "#define", a TOKEN_DIRECTIVE, begins the line. Anything can follow when declaring a macro.
			if(s_bIsDeclaringMacro)
			{
				// for symbols of other macros...
				RegisterToken(TOKEN_MACRO, s_pCurrentToken)->SetValue(s_pMacro);
			}
			break;
		case TOKEN_MACROPARAM:
			// For function-like macros - for variadic params ... is prepended to the name of the identifier which references the params
			if(s_bIsDeclaringMacro)
			{
				RegisterToken(TOKEN_MACROPARAM, s_pCurrentToken)->SetValue(s_pMacroParam);
			}
			else
			{
				// do this in GetIdentifier
			}
			break;
		case TOKEN_VAR:
			// A variable. Most probably part of a command or operator sequence, but we always have suprises...
			pToken = ProcessVariable();
			break;
		}*/
	}
	while(s_bWeAreBeingFedByMacro || !IsLineAtEnd());

	// Bit of optimisation and cleanup
	DoLineFinalPass();

	// Finish with the macro
	if(s_bIsDeclaringMacro)
	{
		s_pDeclaredMacro->FinishDeclaring();
		s_bIsDeclaringMacro = false;
	}

	// Do we have anything in the current line of tokens after that?
	if(pToken = RecallToken(0))
	{
		NextLine();
	}

#if FALSE
	// Now for the real parsing of the tokens we've created
	if(pToken = RecallToken(0))
	{
		if(s_bIsDeclaringMacro)
		{
			// We're declaring a macro.. optimise the input and remove tokens from line
			DoLineFinalPass();

			for(int i=0; CToken *pToken = RecallToken(i); i++)
			{
				SetToken(i, nullptr);						// nothing to see here...

				// Clear string status of any string as it may become an identifier later
				if(pToken->GetType() == TOKEN_STRING)
					pToken->SetValueType(PT_NONE);

				s_pDeclaredMacro->AddToken(pToken);
			}

			s_pDeclaredMacro->FinishDeclaring();
			s_bIsDeclaringMacro = false;
		}
		else if(!s_bIsInHexStruct && !s_bIsInAsmStruct)
		{
			if(s_bExpectAnything)
			{
				NextLine();
				return;
			}

			// The type of the first token sets the route of our parsing
#pragma message("Allow for more inline stuff by separating use of certain tokens")
			switch(pToken->GetType())
			{
			case TOKEN_VAR:
			case TOKEN_VARINDEX:
			case TOKEN_OPERATOR:
			case TOKEN_CONSTRUCT:
			case TOKEN_COMMAND:
				ParseLine();
				DoLineFinalPass();

				// Must save enough lines to add commands for extending any switch
				if(pToken->GetType() == TOKEN_CONSTRUCT && pToken->GetConstruct() == SCR_CASE)
				{
					// Get the case and when commands
					CCommand	* pCommand = CCommands::GetForParsing(SCRIPT_SWITCH),
								* pCaseCmd = CCommands::GetForParsing(SCRIPT_CASE);

					// Get the number of labels in each of the commands + deduce from the max number of switch labels
					int	n		= (CLimits::GetMaxSwitchSize() - pCommand->GetNumCompatibleArgs(CSyntax::GetExtendedType(ET_LABEL)) - 1),
						nParams = pCaseCmd->GetNumCompatibleArgs(CSyntax::GetExtendedType(ET_LABEL));
	
					if(n && nParams)
					{
						// Divide the number of switch labels by the amount of labels per CASES command,
						// so now we have the number of lines to save for all the cases
						n /= nParams;
						while(n--) NextLine();
					}
				}
			
				NextLine();
				break;
			case TOKEN_LABEL:
				NextLine();
				break;
			default:
				if(pToken->GetType() != TOKEN_NONE && !pToken->IsSkippedDuringCompilation())
					THROW("Unexpected identifier '%s'", pToken->GetString());
				break;
			}
		}
		else
		{
			NextLine();
		}
	}
	else if(s_bIsDeclaringMacro)
	{
		s_pDeclaredMacro->FinishDeclaring();
		s_bIsDeclaringMacro = false;
	}
#endif
}

void CParser::ProcessLine()
{
	// ASM line or something for the preprocessor? Then dont mess with the line!
	if(s_bIsInAsmStruct && *s_pLineCurrent != '#')
		return;

	// Mess with the line
	for(char *str = s_pLineCurrent; str < s_pLineEnd; str++)
	{
		// We're looking for strings here...
		if(!s_bStringActive)
		{
			// Beginning a "string"? Dont case convert. If not, check case conversion option...
			if(*str == '"')
			{
				*str = STRCHAR_BEGIN;
				s_bStringActive = true;
				s_nNumStringLiteralChars = 0;
			}
			else
			{
				// Do other stuff that needs doing to a non-stringy part of the line
				switch(SCRambl.Config.Parser.GetCaseConversion())
				{
				case CASE_CONVERT_UPPER:
					*str = ToUpper(*str);
					break;
				case CASE_CONVERT_LOWER:
					*str = ToLower(*str);
					break;
				}
			}

			// Fixup connected operators ('expr0++' to 'expr0 ++' - so we dont parse them as one)
			if((*str < STRCHAR_BEGIN || *str > STRCHAR_END) && IsIdentifierChar(*str))
			{
				if(IsOperatorChar(str[-1]) && IsOperatorChar(str[-2]))
				{
					// Just passed an operator, move all the rest of the characters up one and add a space here
					for(char *tmpStr = s_pLineEnd; tmpStr > str; tmpStr--)
					{
						*tmpStr = tmpStr[-1];
					}
					*str = ' ';
					++s_pLineEnd;
				}
				else if(IsOperatorChar(str[1]) && IsOperatorChar(str[2]))
				{
					// Operator oncoming. Move everything forward and add a space here
					for(char *tmpStr = s_pLineEnd; tmpStr > str+1; tmpStr--)
					{
						*tmpStr = tmpStr[-1];
					}
					str[1] = ' ';
					++s_pLineEnd;
				}
			}
		}
		else
		{
			// Temporarily convert special symbols to some value so the cutting process doesn't nullify them
			switch(*str)
			{
			case ' ':
				*str = STRCHAR_SPACE;
				break;
			case '/':
				*str = STRCHAR_SLASH;
				break;
			case ',':
				*str = STRCHAR_COMMA;
				break;
			case '(':
				*str = STRCHAR_LBRACE;
				break;
			case ')':
				*str = STRCHAR_RBRACE;
				break;
			case '\t':
				*str = STRCHAR_TAB;
				break;
			case ':':
				*str = STRCHAR_COLON;
				break;
			case '\\':
				if(str[-1] != STRCHAR_BSLASH)
					*str = STRCHAR_BSLASH;
				else
					*str = '\\';
				break;
			case '"':
				// Check for preceeding backslash to enter a quote or end the string if there wasn't one
				if(str[-1] == STRCHAR_BSLASH)
					*str = STRCHAR_QUOTE;
				else
				{
					*str = STRCHAR_END;
					s_bStringActive = 0;
					s_bJustOutOfString = true;
				}
				break;
			}

			// Finished with this string? Add it to a list of literal strings
			if(*str != STRCHAR_END) s_szStringLiteral[s_nNumStringLiteralChars++] = *str;

			// Dont overflow us
			if(s_nNumStringLiteralChars > sizeof(s_szStringLiteral))
				THROW("String literal exceeds maximum parsable length of %d chars", sizeof(s_szStringLiteral));
		}
	}
}
void CParser::ProcessDirective()
{
	// Apparently directives in control structs == baaddd? Might need to look into the necessity of this...
	//if(s_nLastParseID >= 0 && s_nLastParseID != SCRIPT_ELSE_GOTO && s_nLastParseID != SCRIPT_GOTO)
		//throw("Unexpected directive");

	// Get directive ID
	int nDirectiveID = CSyntax::GetDirective(s_pCurrentToken);

	// If we're in an inactive preprocessor block and this isn't an #endif or #else, do nothing
	if(s_bPreprocessorLogicHistory[s_nPreprocessorControlDepth] || nDirectiveID == DIRECTIVE_ENDIF || nDirectiveID == DIRECTIVE_ELSE)
	{
		// Directly direct to the directives direction... is 'direct' a real word?
		switch(nDirectiveID)
		{
		case DIRECTIVE_PUSH_CONFIG:
			if(GetTokenData(TOKEN_STRING))
			{
				auto szSett = s_pCurrentToken;
				if(GetTokenData(TOKEN_STRING))
				{
					auto szVal = s_pCurrentToken;
					if(!SCRambl.Config.Parser.PushConfig(szSett, szVal))
						Warning(SCRambl.GetText("WARNINGS", "1005"));
				}
				else throw(SCRambl.GetText("ERRORS", "E1145"));
			}
			else throw(SCRambl.GetText("ERRORS", "E1144"));
			break;
		case DIRECTIVE_POP_CONFIG:
			if(GetTokenData(TOKEN_STRING))
			{
				if(!SCRambl.Config.Parser.PopConfig(s_pCurrentToken))
					Warning(SCRambl.GetText("WARNINGS", "1005"));
			}
			else throw(SCRambl.GetText("ERRORS", "E1146"));
			break;
		case DIRECTIVE_WARNING:
			if(GetTokenData(TOKEN_STRING)) Warning(s_pCurrentToken);
			else throw(SCRambl.GetText("ERRORS", "E1029"));
			break;
		case DIRECTIVE_ERROR:
			if(GetTokenData(TOKEN_STRING)) throw(s_pCurrentToken);
			else throw(SCRambl.GetText("ERRORS", "E1029"));
			break;
		case DIRECTIVE_INCLUDE:			// #include
			DoIncludeFile();
			break;
		case DIRECTIVE_IMPORT:			// #import
			DoImportFile();
			break;

		case DIRECTIVE_CONST_BEGIN:		// #const
			// Keep const structure top-level
			if(s_bIsInControlStruct) throw(SCRambl.GetText("ERRORS", "E1030"));
			// Nah, we cant have this type scoped (yet), sorry.
			if(s_pScript->IsScopeOpen()) throw(SCRambl.GetText("ERRORS", "E1031"));
			// can't be nested either, how boring...
			if(s_bIsInConstStruct) throw(SCRambl.GetText("ERRORS", "E1032"));

			// Tell everyone
			s_bIsInConstStruct = true;
			break;

		case DIRECTIVE_CONST_END:		// #endconst
			if(!s_bIsInConstStruct) throw(SCRambl.GetText("ERRORS", "E1033"));

			// Tell everyone
			s_bIsInConstStruct = false;
			break;

		case DIRECTIVE_HEX_BEGIN:		// #hex
			if(s_bIsInHexStruct) throw(SCRambl.GetText("ERRORS", "E1034"));
					
			// May need this later
			s_nHexBeginLine = s_nNumLinesParsed;
					
			// Tell everyone
			s_bIsInHexStruct = true;
			break;

		case DIRECTIVE_HEX_END:			// #endhex
			if(!s_bIsInHexStruct) throw(SCRambl.GetText("ERRORS", "E1035"));

			// Tell everyone
			s_bIsInHexStruct = false;
			break;

		case DIRECTIVE_ASM_BEGIN:		// #asm
			if(s_bIsInAsmStruct) throw(SCRambl.GetText("ERRORS", "E1036"));

			// Setup an ASM struct structure with info we need for the assembler (assembles in preprocessor time)
			s_nAsmBeginLine = s_nNumLinesParsed;
			s_nAsmStructID = s_nNumAsmStructs + 1;
			s_pAsmStruct = &s_AsmStructs[s_nNumAsmStructs];
			s_pAsmStruct->m_nStartLine = s_nAsmBeginLine;
			s_pAsmStruct->m_bIsAssembled = false;

			// TOKEN_ASM will notify the compiler that an ASM block begins here
			RegisterToken(TOKEN_ASM)->SetValue(s_nNumAsmStructs);

			// Tell everyone
			s_bIsInAsmStruct = true;
			break;

		case DIRECTIVE_ASM_END:			// #endasm
			if(!s_bIsInAsmStruct) throw(SCRambl.GetText("ERRORS", "E1037"));

			// The struct will help later
			s_pAsmStruct->m_nEndLine = s_nNumLinesParsed;
			s_nNumAsmStructs++;

			NextLine();

			// Tell everyone
			s_bIsInAsmStruct = false;
			break;

		case DIRECTIVE_DEFINE:			// #define
			// Tell everyone
			s_bIsDeclaringMacro = true;
			s_bMacroIsFunctiony = false;

			// Get the name of the macro
			if(!GetTokenData()) throw(SCRambl.GetText("ERRORS", "E1038"));

			// We dont need to check the identifier is a string - macros are allowed to override anything
			if(!(s_pDeclaredMacro = CMacros::Add(s_pCurrentToken)))
				// Freak out
				throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1039"), NULL, 0, "%s", s_pCurrentToken));
			
			// Does the declared macro look function-y?
			if(s_bMacroIsFunctiony)
			{
				GetTokenData(TOKEN_STRING);
				const char * pStr = s_pCurrentToken;
				bool bParamDefined = false;
				bool bIsVariadic;
				while(true)
				{
					char szTempString[MAX_IDENTIFIER_NAME];
					pStr = ltrim(pStr);
					switch(*pStr)
					{
					case '\0': throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1040"), NULL, 0, "%c", PUNC_RBRACKET));
					case PUNC_RBRACKET:
						break;
					case PUNC_COMMA:
						if(!bParamDefined) throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1041"), NULL, 0, "%c", PUNC_COMMA));
						++pStr;
						bParamDefined = false;
						continue;
					default:
						// really no use in variadic macro's whilst there's no ( ) to limit the number of params
#if 0
						if(*pStr == PUNC_PERIOD && pStr[1] == PUNC_PERIOD && pStr[2] == PUNC_PERIOD)
						{
							bIsVariadic = true;
							pStr = ltrim(pStr + 3);
						}
						else bIsVariadic = false;
#else
						bIsVariadic = false;
#endif
						pStr = CSyntax::ReadIdentifier(pStr, szTempString);
						if(!pStr) throw(SCRambl.GetText("ERRORS", "E1042"));
						s_pDeclaredMacro->AddParam(szTempString)->IsVariadic(bIsVariadic);
						bParamDefined = true;
						continue;
					}
					break;
				}
			}
			if(*++s_pLineCurrent)
			{
				auto pToken = new CToken(TOKEN_STRING, s_pLineCurrent);
				s_pLineCurrent += pToken->GetStringLength();
				s_pDeclaredMacro->AddToken(pToken);
			}
			break;

		case DIRECTIVE_UNDEF:			// #undef
			{
				s_pLineCurrent += sizeof("UNDEF");
				bool bPrevDontFeed = s_bDontFeedFromMacro;
				s_bDontFeedFromMacro = true;
				if(GetTokenData(TOKEN_MACRO) == TOKEN_MACRO && s_pMacro)
				{
					CMacros::Remove(s_pMacro);
					s_pMacro = nullptr;
				}
				else throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1043"), NULL, 0, "%s", s_pCurrentToken));
				s_bDontFeedFromMacro = bPrevDontFeed;
				break;
			}

		case DIRECTIVE_IF:				// #if
			s_pLineCurrent += sizeof("IF");

			// Do a little manual line interpretation work
			if(!*s_pLineCurrent) throw(SCRambl.GetText("ERRORS", "E1044"));
		
			// EvaluateExpression is the most awsum func ever - do some const integer expression stuff
			if(!(s_bPreprocessorLogicHistory[++s_nPreprocessorControlDepth] = (EvaluateExpression(s_pLineCurrent) ? 1 : 0)))
				// We still need to read through tokens following a false #if, but we dont need to expand macros at least...
				s_bDontFeedFromMacro = true;
		
			s_pLineCurrent = s_pLineEnd;

			// Tell everyone
			s_bInPreprocessorControlStruct = true;
			s_bIsInLoopingPreprocessorBlock = false;
			break;
	
		case DIRECTIVE_ELSE:			// #else
			if(!s_nPreprocessorControlDepth) throw(SCRambl.GetText("ERRORS", "E1045"));
			if(s_bIsInLoopingPreprocessorBlock) throw(SCRambl.GetText("ERRORS", "E1046"));

			// Do a switcharoo
			s_bPreprocessorLogicHistory[s_nPreprocessorControlDepth] = !s_bPreprocessorLogicHistory[s_nPreprocessorControlDepth];

			// If we expanded macros in a true #if, dont do it in this #else!
			s_bDontFeedFromMacro = !s_bPreprocessorLogicHistory[s_nPreprocessorControlDepth];
			break;

		case DIRECTIVE_ENDIF:			// #endif
			if(!s_nPreprocessorControlDepth) throw(SCRambl.GetText("ERRORS", "E1047"));
			if(s_bIsInLoopingPreprocessorBlock) throw(SCRambl.GetText("ERRORS", "E1046"));

			// Revert it back to false
			s_bPreprocessorLogicHistory[s_nPreprocessorControlDepth--] = false;

			// Go back to the previous preprocessor control setting for macro expansion
			s_bDontFeedFromMacro = !s_bPreprocessorLogicHistory[s_nPreprocessorControlDepth];

			// Tell everyone
			s_bIsInLoopingPreprocessorBlock = s_bPreprocessorLogicHistory[s_nPreprocessorControlDepth] > 1;
			s_bInPreprocessorControlStruct = s_nPreprocessorControlDepth > 0;
			break;

		case DIRECTIVE_EACH:			// #each
			throw("#each not fully implemented");
			s_pLineCurrent += sizeof("EACH");

			// Each what?
			if(!*s_pLineCurrent) throw(SCRambl.GetText("ERRORS", "E1044"));

			// Evaluate the number of times to loop this new preprocessor block
			if(!(s_bPreprocessorLogicHistory[++s_nPreprocessorControlDepth] = EvaluateExpression(s_pLineCurrent)))
				// If none, we're like a false #if
				s_bDontFeedFromMacro = true;

			// that's all, folks
			s_pLineCurrent = s_pLineEnd;

			// tell em all
			s_bInPreprocessorControlStruct = true;
			s_bIsInLoopingPreprocessorBlock = true;
			s_nPreprocessorLoopStart = s_nNumLinesParsed;
			break;

		case DIRECTIVE_ENDEACH:			// #endeach
			if(!s_bIsInLoopingPreprocessorBlock) throw(SCRambl.GetText("ERRORS", "E1046"));
			
			// For each...
			/*while(--s_bPreprocessorLogicHistory[s_nPreprocessorControlDepth])
			{
				for(int x=s_nPreprocessorLoopStart,l=s_nNumLinesParsed; x<l; ++x)
				{
					for(int y=0; auto pToken = GetToken(x, y); ++y)
					{
						int i;
						switch(pToken->GetType())
						{
						case TOKEN_SCRAMBL_REGISTER:
							i = pToken->GetIntValue();
							break;
						default:
							break;
						}
						AddToken(pToken);
					}
					NextLine();
				}
			}*/

			// Back up
			s_bDontFeedFromMacro = !s_bPreprocessorLogicHistory[--s_nPreprocessorControlDepth];

			// Declare
			s_bIsInLoopingPreprocessorBlock = s_bPreprocessorLogicHistory[s_nPreprocessorControlDepth] > 1;
			s_bInPreprocessorControlStruct = s_nPreprocessorControlDepth > 0;
			break;

		case DIRECTIVE_REGISTER_COMMAND:		// #register_command
			DoRegisterCommand();
			break;

		case DIRECTIVE_REGISTER_TYPE:		// #register_type
			DoRegisterType();
			break;

		case DIRECTIVE_REGISTER_VAR:		// #register_var
			DoRegisterVar();
			break;
			
		case DIRECTIVE_HEADER:		// #header
			if(s_bIsInHeader) throw(SCRambl.GetText("ERRORS", "E1048"));
			s_pScript->AddHeader();
			s_bIsInHeader = true;
			break;
		case DIRECTIVE_ENDHEADER:		// #endheader
			if(!s_bIsInHeader) throw(SCRambl.GetText("ERRORS", "E1049"));
			s_bIsInHeader = false;
			break;

		case DIRECTIVE_CONSOLE:			// #console
			if(s_bInConsoleMode) throw(SCRambl.GetText("ERRORS", "E1050"));
			s_bInConsoleMode = true;
			break;
		case DIRECTIVE_ENDCONSOLE:		// #endconsole
			if(!s_bInConsoleMode) throw(SCRambl.GetText("ERRORS", "E1051"));
			s_bInConsoleMode = false;
			break;

		case DIRECTIVE_FUNCTION:		// #function
			if(s_pSubScript->IsScopeOpen()) throw(SCRambl.GetText("ERRORS", "E1052"));
			if(!s_bInConsoleMode) throw(SCRambl.GetText("ERRORS", "E1053"));

			DoFunctionBegin();
			break;
		case DIRECTIVE_ENDFUNCTION:		// #endfunction
			if(!s_pSubScript->IsScopeOpen()) throw(SCRambl.GetText("ERRORS", "E1054"));
			if(!s_bInConsoleMode) throw(SCRambl.GetText("ERRORS", "E1055"));

			DoFunctionEnd();
			break;

		default:
			// Nope, dont know this trick yet...
			throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1056"), NULL, 0, "%s", s_pCurrentToken));
		}
	}
}
CToken * CParser::ProcessConstruct()
{
	switch(s_nConstructID)
	{
	case SCR_AND:
	case SCR_OR:
		if(s_nLastParseID != SCRIPT_IF) throw(SCRambl.GetText("ERRORS", "E1057"));
		if(s_bLogicalOperator == -1) s_bLogicalOperator = s_nConstructID == SCR_AND ? LOGICAL_AND : LOGICAL_OR;
		else if(s_bLogicalOperator != (s_nConstructID == SCR_AND ? LOGICAL_AND : LOGICAL_OR)) throw(SCRambl.GetText("ERRORS", "E1058"));
		++s_nNumConditions;
		s_bLineContinuesConditions = true;
		break;
	case SCR_NOT:
		s_bLogicalOperatorNot = true;
		break;
	default:
		throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1059"), NULL, 0, "%s", s_pCurrentToken));
		return nullptr;
	}
	auto pToken = RegisterToken(TOKEN_CONSTRUCT, s_pCurrentToken);
	pToken->SetValueType(PT_NONE);
	pToken->SetConstruct(s_nConstructID);
	return pToken;
}
CToken * CParser::ProcessHexToken()
{
	CToken * pToken;
	if(s_eTokenType == TOKEN_HANDLED)
		pToken = s_pToken;
	switch(s_eTokenType)
	{
	case TOKEN_COMMAND:
		pToken = RegisterToken(TOKEN_HEX_WORD, s_pCurrentToken);
		pToken->SetValue(s_bLogicalOperatorNot ? s_pCommand->GetId() | LOGICAL_NOT : s_pCommand->GetId());
		pToken->SetValueType(CSyntax::GetType(TYPE_INT));
		break;
	case TOKEN_VAR:
		pToken = RegisterToken(TOKEN_HEX_WORD, s_pCurrentToken);
		pToken->SetValue(s_pVariable->GetIndex());
		pToken->SetValueType(CSyntax::GetType(TYPE_INT));
		break;
	case TOKEN_VALUE:
		if(!IsStringHex(s_pCurrentToken))
		{
			pToken = RegisterToken(TOKEN_VALUE);
			if(IsStringInt(s_pCurrentToken))
			{
				pToken->SetValue(StringToInt(s_pCurrentToken));
				pToken->SetValueType(CSyntax::GetType(TYPE_INT)); 
			}
			else if(IsStringFloat(s_pCurrentToken))
			{
				pToken->SetValue(StringToFloat(s_pCurrentToken));
				pToken->SetValueType(CSyntax::GetType(TYPE_FLOAT));
			}
			else throw(SCRambl.GetText("ERRORS", "E1060"));
			break;
		}
	case TOKEN_HEX:
		if(auto pConst = CConstants::Get(s_pCurrentToken))
		{
			pToken = RegisterToken(TOKEN_VALUE);
			pToken->SetValue(pConst->GetValue());
			pToken->SetValueType(CSyntax::GetType(TYPE_INT));
		}
		else
		{
			if(IsStringHex(s_pCurrentToken))
			{
				char hex[4];
				int len = strlen(s_pCurrentToken);
				if(len % 2) throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1061"), NULL, 0, "%s", s_pCurrentToken));
						
				// Parse hex sequence...
				auto pType = CSyntax::GetType(TYPE_INT);
				do {
					szncpy(hex, s_pCurrentToken, 3);
					pToken = RegisterToken(TOKEN_HEX_BYTE, hex);
					pToken->SetValue(StringToHex(hex));
					pToken->SetValueType(pType);
					s_pCurrentToken += 2;
					len -= 2;
				} while(*s_pCurrentToken);
			}
			else
			{
				// this might be a label that is yet to occur.
				// we dont know for sure as there's no expected param type, so just store it as this temporarily...
				pToken = RegisterToken(TOKEN_HEXMAYBELABEL, s_pCurrentToken);
				pToken->SetValue(s_nNumCodeLines);
				s_MaybeLabelTokens.Add(GenerateHash(pToken->GetString()), pToken);
			}
		}
		break;
	case TOKEN_STRING:
		// Untranslated string
		if(*s_pCurrentToken == STRCHAR_BEGIN) ++s_pCurrentToken;
		pToken = RegisterToken(TOKEN_STRING, s_pCurrentToken);
		pToken->SetValue(pToken->GetStringLength());
		pToken->SetValueType(CSyntax::GetType(TYPE_STRING));
		break;
	case TOKEN_LABEL:
		// A label in hex should be handled the same
		pToken = ProcessLabel(true);
		break;
	default:
		throw(SCRambl.GetText("ERRORS", "E1062"));
	case TOKEN_NONE:
		pToken = nullptr;
		break;
	}
	return pToken;
}
void CParser::ProcessEndOfConditionList()
{
	// This code will run on the line after the conditions of an IF/WHILE statement (when AND/OR first absent)
	// We need to set the number of conditions in the IF param we stored, 'enter' the construct and
	// write the ELSE_GOTO code to jump to ENDIF or ELSE
	if(s_nNumConditions >= MAX_CONDITIONS)
		throw(SCRambl.GetText("ERRORS", "E1063"));

	// Apply the logical operator
	if(s_bLogicalOperator >= 0)
		s_nNumConditions += s_bLogicalOperator;

	// Get the label to ELSE_GOTO or top of WHILE
	s_pLabel = s_pControlStructure->GetEndLabel();//s_pControlStructure->IsLoop() ? s_pControlStructure->GetBeginLabel() : s_pControlStructure->GetEndLabel();
	s_pLabel->IsReferenced(true);
	s_pLabel->IsInternal(true);
	
	// write else/end jump for IF statement
	auto pCommand = GetParseCommand(SCRIPT_ELSE_GOTO);
	auto pToken = RegisterToken(TOKEN_COMMAND, pCommand->GetName());
	pToken->SetValue(pCommand);

	// write else/end jump label for IF statement
	pToken = RegisterToken(TOKEN_LABEL, s_pLabel->GetName());
	pToken->SetValue(s_pLabel);
	pToken->SetValueType(CSyntax::GetExtendedType(ET_LABEL));
	TranslateToken(pToken);
	NextLine();

	// set num conditions param
	pToken = s_pControlStructure->GetToken(0);
	pToken->SetValue(s_nNumConditions - 1);
	pToken->SetValueType(CSyntax::GetType(TYPE_INT));
	pToken->SetString(tostr(s_nNumConditions - 1));
	TranslateToken(pToken);
	
	// Enter control structure
	++s_nControlStructDepth;
	s_nLastParseID = -1;
	s_bLogicalOperator = -1;
	s_bLogicalOperatorNot = false;
}
void CParser::ProcessTypeName()
{
	// (so var declarations only work with TYPENAME beginning the line and not during a command param sequence)
#pragma message("IMPORTANT: Move TOKEN_TYPENAME type declaration processing to postprocessor after command/operator processing")

	// Convert this typename to a CParamType
	auto pType = CSyntax::GetTypeByName(s_bWeAreBeingFedByMacro ? s_pTypeName : s_pCurrentToken);

	// We cant use a non-variable type here (should only be able to do so in const structs)
	if(!pType->IsAnyVar()) throw(SCRambl.GetText("ERRORS", "E1064"));

	// Thats not the way to do it...
	if(pType->IsAnyArray()) throw(SCRambl.GetText("ERRORS", "E1065"));

	// Get the next identifier. It may be a future var, but should be a string right now
	if(eTokenType type = GetTokenData())
	{
		do
		{
			int nArraySize = 0;
			int nResult;

			// TOKEN_STRING? Good. TOKEN_VAR? The variable already exists!
			switch(type)
			{
			case TOKEN_STRING:
				// Figure out just what we want from this var. Look for array bracket '['...
				if(nResult = CSyntax::GetArrayIndexAndTokenize(s_pLineTokens[s_nNumTokens-1], nArraySize))
				{
					// Validate the array declaration
					if(nArraySize > 0)
					{
						if((DWORD)nArraySize > CLimits::GetMaxArraySize())
							throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1066"), NULL, 0, "%d%d", nArraySize, CLimits::GetMaxArraySize()));
						if(nArraySize == 1) Warning(SCRambl.GetText("WARNINGS", "1002"));
					}
					else throw(SCRambl.GetText("ERRORS", "E1067"));
				}

				// Add the variable/array!
				if(s_pVariable = CVariables::Add(s_pLineTokens[s_nNumTokens-1], pType, 0, nArraySize))
				{
					// Add to the script list (or subscript for scoped vars)
					s_pScript->Add(s_pVariable);
					s_pVariable->SetDeclarationLine(s_nNumCodeLines);
				}
				break;

			case TOKEN_VAR:
				// Just so we can display a more relevant error msg...
				throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1068"), NULL, 0, "%s%d", s_pVariable->GetName(), s_pVariable->GetDeclarationLine()));
					
			default:
				// Dont be passing anything else, please...
				throw(SCRambl.GetText("ERRORS", "E1069"));
			}
		}
		// Oh, there's more?
		while(type = GetTokenData());
	}
	else Warning(SCRambl.GetText("WARNINGS", "1003"));
}
CToken * CParser::ProcessCommand()
{
	CToken * pToken = nullptr;

	// We'll parse macro'd commands when they're used
	if(!s_bIsDeclaringMacro)
	{
		// Got anything to parse?
		switch(s_pCommand->GetParseFunc())
		{
		case SCRIPT_IF:
			// Add a label for this IFs ELSE...END
			CreateLabelForIF();

			// Add a control structure
			CreateControlStructIF(s_pLabel);

			// An IF has an invisible value token which we'll set internally
			CreateCodeForIF();

			// Set IF stats
			s_nLastParseID = SCRIPT_IF;
			s_nNumConditions = 1;
			s_bLogicalOperator = -1;
			s_bLineContinuesConditions = true;
			return nullptr;
		case SCRIPT_ENDIF:
			if(SCRambl.Config.Optimisation.SkipUselessIFs())
			{
				if(s_pControlStructure->GetToken(0)->GetIntValue() == 0)
				{
					s_pControlStructure->GetToken(0)->IsSkippedDuringCompilation(true);
					s_pControlStructure->GetToken(1)->SetValue(GetParseCommand(SCRIPT_IF));
					s_pControlStructure->GetToken(1)->IsSkippedDuringCompilation(true);
				}
			}

			CreateCodeForENDIF();
			CloseControlStructure();
			return nullptr;
		case SCRIPT_REPEAT:
			{
				if(GetTokenData() != TOKEN_VALUE)
					throw(SCRambl.GetText("ERRORS", "E1070"));
				if(!IsStringInt(s_pCurrentToken))
					throw(SCRambl.GetText("ERRORS", "E1070"));
				const char * szCountr = s_pCurrentToken;
				if(GetTokenData(TOKEN_VAR) != TOKEN_VAR)
					throw(SCRambl.GetText("ERRORS", "E1071"));

				CreateControlStructREPEAT(s_pVariable, szCountr);
				CreateCodeForREPEAT();
				CreateLabelForREPEAT();
				s_nLastParseID = SCRIPT_REPEAT;
			}
			return nullptr;
		case SCRIPT_ENDREPEAT:
			if(!s_pControlStructure || s_pControlStructure->GetConstruct() != SCR_REPEAT)
				throw(SCRambl.GetText("ERRORS", "E1072"));
			
			CreateCodeForENDREPEAT();
			CloseControlStructure();
			return nullptr;
		case SCRIPT_WHILE:
			sprintf(g_szTempString, "__SPECIAL_LABEL_WHILES_A_%d", s_nNumWHILEs++);
			CreateInternalLabel(g_szTempString);
			CreateControlStructWHILE(s_pLabel);
			sprintf(g_szTempString, "__SPECIAL_LABEL_WHILES_B_%d", s_nNumWHILEs++);
			CreateInternalLabel(g_szTempString);
			s_pControlStructure->SetEndLabel(s_pLabel);
			CreateCodeForWHILE();
			s_nLastParseID = SCRIPT_IF;
			s_nNumConditions = 1;
			s_bLogicalOperator = -1;
			s_bLineContinuesConditions = true;
			return nullptr;
		case SCRIPT_ENDWHILE:
			if(!s_pControlStructure || s_pControlStructure->GetConstruct() != SCR_WHILE)
				throw(SCRambl.GetText("ERRORS", "E1073"));

			CreateCodeForENDWHILE();
			CloseControlStructure();
			return nullptr;
		case SCRIPT_ELSE:
			ParseConstructElse();
			return nullptr;
		case SCRIPT_CASE:
			ParseSwitchBegin();
			return nullptr;
		case SCRIPT_WHEN:
			ParseCaseBegin();
			return nullptr;
		case SCRIPT_ENDCASE:
			ParseSwitchEnd();
			return nullptr;
		case SCRIPT_NAME:
			s_nLastParseID = SCRIPT_NAME;
			break;
		default:
			break;
		}
	}

	// Just register a normal (boring) command
	pToken = RegisterToken(TOKEN_COMMAND, s_pCurrentToken);
	pToken->SetValue(s_pCommand);
	return pToken;
}
CToken * CParser::ProcessOperator()
{
	auto pToken = RegisterToken(TOKEN_OPERATOR, s_pCurrentToken);
	pToken->SetValue(s_pOperator);
	return pToken;
}
void CParser::ProcessArgs(CToken *pCommandToken)
{
	if(s_bIsInPreprocessorDirective) return;
	if(!pCommandToken->GetOpcodeValue()) return;

	static CLListEx<const CCommandArg*>						s_ArgList;
	static CLListEx<const CCommand*>						s_Commands;
	static CLListEx<const COperation*>						s_Operations;
	static CLListEx<CLinkPtr<const CCommandArg*>>			s_ArgPtrs;
	
	bool	bIsCommand		= pCommandToken->GetType() != TOKEN_OPERATOR;
	auto	pCommand		= pCommandToken->GetCommandValue();
	auto	pOperator		= pCommandToken->GetOperatorValue();
	auto	pArgs			= bIsCommand ? pCommand->GetArgs() : &s_ArgList;
	bool	bOverloaded		= bIsCommand ? pCommand->IsOverloaded() : pOperator->GetNumOperations() > 1;
	int		nNumOverloads	= 0,
			nMaxArgs		= -1,
			nMinArgs		= -1;
	
	const COperation	*	pOperation = nullptr;
	CToken				*	pLToken = nullptr,
						*	pRToken = nullptr;

	if(bOverloaded)
	{
		// Overload ourselves with commands or operations to slowly narrow down to one
		if(bIsCommand)
		{
			s_ArgPtrs.Clear();
			s_ArgPtrs.Init();
			s_Commands.Clear();
			s_Commands.Init();
			do {
				s_Commands.Add(pCommand);
				s_ArgPtrs.Add(pCommand->GetArgs()->Begin());
				if(pCommand->GetNumArgs() > nMaxArgs || nMaxArgs == -1) nMaxArgs = pCommand->GetNumArgs();
				if(pCommand->GetNumArgs() < nMinArgs || nMinArgs == -1) nMinArgs = pCommand->GetNumArgs();
				++nNumOverloads;
			} while(pCommand = pCommand->GetNextOverload());
			
			// We gotta Begin somewhere...
			pCommand = *s_Commands.Begin();
			pArgs = pCommand->GetArgs();
		}
		else
		{
			// Depending on how many operations are on the line, we're basically building one long dynamic command arg list
			pOperation = *pOperator->GetOperations()->Begin();
		}
	}
	else if(!bIsCommand)
	{
		pOperation = *pOperator->GetOperations()->Begin();
	}
	else if(!pCommand->GetNumArgs()) return;

	// The overlord loop
	int nArg = 0;
	bool bRightOperand = s_nBaseVarTokenIdx != -1;
	bool bAnotherOperator = false;
	bool bAlreadyDoneSir = false;
	bool bStartFresh = false;
	bool bInNumArgs = false;
	int nNumArgsLeft = 0;

	// Do we have a post-unary? Then we only need to do one thing...
	if(bRightOperand && pOperation->GetRightArg()->GetDefault())
	{
		auto pBaseVarTok = RecallToken(s_nBaseVarTokenIdx);

		if(!(pOperation = s_nLastParseID == SCRIPT_IF
			? pOperator->GetConditionMatchingType(pBaseVarTok->GetArgType())
			: pOperator->GetOperationMatchingType(pBaseVarTok->GetArgType())))
			throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1074"), NULL, 0, "%s%s", s_pCurrentToken, pBaseVarTok->GetArgType()->GetName()));

		pCommandToken->SetValue(pOperation);

		if(auto pDefault = pOperation->GetRightArg()->GetDefault())
		{
			auto pToken = RegisterToken(pDefault->GetType(), pDefault->GetString());
			pToken->SetValueType(pDefault->GetArgType());
			pToken->SetValue(pDefault->GetIntValue());

			TranslateToken(pBaseVarTok);
			TranslateToken(pToken);

			SetToken(s_nBaseVarTokenIdx++, pCommandToken);
			SetToken(s_nBaseVarTokenIdx, pBaseVarTok);

			bAlreadyDoneSir = true;
			bOverloaded = false;
		}
	}
	
	if(!bAlreadyDoneSir)
	{
		// The almighty loop
		CToken * pVarArgCountToken = nullptr;
		for(auto pLink = bIsCommand ? pArgs->Begin() : pArgs->Bad(); !bIsCommand || pLink != pArgs->Bad(); ++nArg)
		{
			auto pArg				= bIsCommand ? *pLink : (bRightOperand ? pOperation->GetRightArg() : pOperation->GetLeftArg());
			auto	 pTypeBase			= pArg->GetType();
			auto	 pType				= pTypeBase ? pTypeBase->Standardise() : nullptr;
			auto	 pExType				= pTypeBase && pTypeBase->IsExtended() ? pTypeBase->Extend() : nullptr;
			auto	 eExpectedType		= pType ? pType->GetTokenType() : TOKEN_NONE,
					eExpectedExType		= pExType ? pExType->GetTokenType() : eExpectedType;
			eTokenType TokenType = TOKEN_NONE;
			CToken	* pToken = nullptr;
			bool bIsNullArg = false;

			try
			{
				if(bIsCommand)
				{
					if(pLink->IsVarArgs())
					{
						if(pVarArgCountToken)
						{
							pVarArgCountToken->SetValue(pVarArgCountToken->GetIntValue() + 1);
						}
						else
						{
							pVarArgCountToken = RegisterToken(TOKEN_VARARG_NUM_ARGS);
							pVarArgCountToken->SetValue(0);
						}
					}
					else if(pVarArgCountToken)
					{
						pVarArgCountToken = nullptr;
					}
				}

				if(pArg->IsReturn()) eExpectedExType = eExpectedType = TOKEN_VAR;

				if(pTypeBase && pTypeBase->GetType() == PT_NONE) bIsNullArg = true;

				// Create a token as our mid-way translation of the code - if we only have one possible arg speed things up by 'expecting' a token type
				switch(TokenType = bIsNullArg ? TOKEN_NONE : GetTokenData(!bIsCommand || bOverloaded ? TOKEN_NONE : eExpectedType))
				{
				case TOKEN_OPERATOR:
					if(bIsCommand || bRightOperand)
						throw(SCRambl.GetText("ERRORS", "E1075"));
					
					// Collect the token being operated on and move to a new line
					if(auto pBaseVarTok = RecallToken(s_nBaseVarTokenIdx))
					{
						// If we've not just optimised out a self-assigning operation, go to the next line
						// else act like we've only just been sent from PreparseLine
						if(!bStartFresh)
						{
							//DoLineFinalPass();
							//NextLine();
						}

						// Change operator
						pCommandToken = ProcessOperator();
						pOperator = pCommandToken->GetOperatorValue();
						pOperation = *pOperator->GetOperations()->Begin();
						
						// An operand should follow
						bRightOperand = true;
						bAnotherOperator = !bStartFresh;
						if(bAnotherOperator)
						{
							s_nBaseVarTokenIdx = s_nNumRegisteredTokens;
							AddToken(pBaseVarTok);
						}
					}
					else throw(SCRambl.GetText("ERRORS", "E1076"));
					continue;
				case TOKEN_VALUE:
					pToken = ProcessValue();
					break;
				case TOKEN_VAR:
				case TOKEN_VARINDEX:
					pToken = ProcessVariable();
					break;
				case TOKEN_LABEL:
				case TOKEN_STRING:
					if(eExpectedExType == TOKEN_LABEL)
					{
						pToken = ProcessLabel();
						break;
					}
					else if(eExpectedExType == TOKEN_ENUM)
					{
						if(pExType)
						{
							auto pEnum = pExType->GetEnum();
							if(auto pEnumerator = pEnum->GetEnumerator(s_pCurrentToken))
							{
								pToken = RegisterToken(TOKEN_VALUE, s_pCurrentToken);
								pToken->SetValue(pEnumerator->GetValue());
								pToken->SetValueType(pExType);
							}
							else if(auto pConst = CConstants::Get(s_pCurrentToken))
							{
								// If a constant is defined with an enumerator from the right enum, we can treat it like an alternative enumerator ID
								if(pConst->IsEnumerator() && pConst->Enumerate()->GetEnum() == pEnum)
								{
									pToken = RegisterToken(TOKEN_VALUE, s_pCurrentToken);
									pToken->SetValue(pConst->GetValue());
									pToken->SetValueType(pExType);
								}
								else if(!bOverloaded) throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1077"), NULL, 0, "%s", pExType->GetName()));
							}
							else if(!bOverloaded) throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1077"), NULL, 0, "%s", pExType->GetName()));
							break;
						}
					}
					else if(eExpectedExType == TOKEN_VALUE)
					{
						// Try to get a constant if we're expecting a value
						if(auto pConst = CConstants::Get(s_pCurrentToken))
						{
							pToken = RegisterToken(TOKEN_VALUE, s_pCurrentToken);
							pToken->SetValue(pConst->GetValue());
							pToken->SetValueType(CSyntax::GetType(TYPE_INT));
							break;
						}
					}
					pToken = ProcessString();
					break;
				case TOKEN_COMMAND:
					if(eExpectedExType == TOKEN_VALUE)
					{
						pToken = RegisterToken(TOKEN_VALUE, s_pCurrentToken);
						pToken->SetValue(s_pCommand->GetId());
						pToken->SetValueType(CSyntax::GetType(TYPE_INT));
						break;
					}
				case TOKEN_TYPENAME:
					if(eExpectedExType = TOKEN_LABEL) pToken = ProcessLabel();
					else pToken = ProcessString();
					break;
				case TOKEN_NONE:
					if(!bIsCommand)
					{
						bOverloaded = false;
						if(bRightOperand)
							throw(SCRambl.GetText("ERRORS", "E1078"));
						break;
					}
					else if(bIsNullArg)		// we at the end of an argument list?
					{
						// add an end-of-argument-list-terminator-thing
						AddToken(s_pEOALToken);
						pToken = s_pEOALToken;
						break;
					}
					else if(pArg->IsVarArgs()) break;
				default:
					throw("?????????");
					break;
				}

				if(pToken)
				{
					if(TokenType != TOKEN_NONE)
						TokenType = pToken->GetType();

					if(!pArg->IsCompatible(pToken->GetArgType()))
						throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", pArg->IsReturn() ? "E1079" : "E1077"), NULL, 0, "%s", pArg->GetType()->GetName()));

					//if(TokenType != TOKEN_VAR && eExpectedExType != TokenType && eExpectedType != TokenType)
					//	THROW(pArg->IsReturn() ? "expected '%s'" : "expected variable for return of '%s'", pArg->GetType()->GetName());
				}
				else if(pArg->IsVarArgs() || pArg->IsNumArgs()) { }
				else if(!bOverloaded) break;
				else throw(SCRambl.GetText("ERRORS", "E1080"));
			}
			catch(const char *e)
			{
				// No overload can help us? Then you sir, did something wrong...
				if(!bOverloaded)
				{
					char szError[128];
					szncpy(szError, e, sizeof(szError));
					throw(SCRambl.BuildText(SCRambl.GetText("PARSER", "M107"), NULL, 0, "%s%s%d", s_pCurrentToken, szError, nArg+1));
				}
			}

			if(bOverloaded)
			{
				if(!bIsCommand)
				{
					if(pToken)
					{
						// We on the left or right operand?
						if(!bRightOperand)
						{
							// If we've got a pre-unary operation, update our choice of operation now
							if(pOperation->GetRightArg()->GetDefault())
							{
								if(!(pOperation = pOperation = s_nLastParseID == SCRIPT_IF
									? pOperator->GetConditionMatchingType(pToken->GetArgType())
									: pOperator->GetOperationMatchingType(pToken->GetArgType())))
									throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1081"), NULL, 0, "%s%s", s_pCurrentToken, pToken->GetArgType()->GetName()));
							}
							
							pCommandToken->SetValue(pOperation);
						
							if(auto pDefault = pOperation->GetRightArg()->GetDefault())
							{
								// Pre-unary operation - skip a step and add the 'default' token
								TranslateToken(pToken);
								pToken = RegisterToken(pDefault->GetType(), pDefault->GetString());
								pToken->SetValueType(pDefault->GetArgType());
								pToken->SetValue(pDefault->GetIntValue());
								TranslateToken(pToken);

								// We're done here
								bOverloaded = false;
								break;
							}
							else
							{
								bRightOperand = true;
							}
						}
						else
						{
							// op1 {operator} op2
							auto pBaseVarTok = RecallToken(s_nBaseVarTokenIdx);

							// Skip self var assignment (op1=op1)?
							if(SCRambl.Config.Optimisation.SkipSelfAssignment() && pOperator->IsAssignment() && 
								pToken->GetType() == TOKEN_VAR && pBaseVarTok->GetVariableValue()->IsNamed(*pToken->GetVariableValue())
							)
							{
								// Remove...
								for(int i=bAnotherOperator ? s_nBaseVarTokenIdx : s_nBaseVarTokenIdx - 1; i <= s_nNumRegisteredTokens; ++i)
								{
									SetToken(i, nullptr);
								}
								
								s_nNumRegisteredTokens = s_nBaseVarTokenIdx + 1;
								SetToken(s_nBaseVarTokenIdx, pBaseVarTok);
								pToken = nullptr;
								bStartFresh = true;
							}
							else
							{
								// Int to float and vice-versa 'cept with warnings
								ConvertTokenType(pToken, pBaseVarTok->GetArgType());
								
								// Get the correct operation
								if(!(pOperation = s_nLastParseID == SCRIPT_IF
									? pOperator->GetConditionMatchingTypes(pBaseVarTok->GetArgType(), pToken->GetArgType())
									: pOperator->GetOperationMatchingTypes(pBaseVarTok->GetArgType(), pToken->GetArgType())))
								{
									throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", pOperator->IsConditional() ? "E1083" : "E1082"), NULL, 0, "%s%s%s",
										s_pCurrentToken, pBaseVarTok->GetArgType()->GetName(), pToken->GetArgType()->GetName()));
								}

								// Update the masta token
								pCommandToken->SetValue(pOperation);

								// else: op1 {operator} op2 {operator} op3...
								if(!bAnotherOperator || bStartFresh)
								{
									SetToken(s_nBaseVarTokenIdx++, pCommandToken);
									SetToken(s_nBaseVarTokenIdx, pBaseVarTok);
									bStartFresh = false;
									TranslateToken(pBaseVarTok);
								}
								else if(pOperator->IsAssignment())
									SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1084"), NULL, 0, "%s", s_pCurrentToken);
								else if(pOperator->IsConditional())
									SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1085"), NULL, 0, "%s", s_pCurrentToken);
							}
							
							bRightOperand = false;
						}
					}
				}
				else if(pToken)
				{
					int nBestMatchLevel = 0;
					bool bRepeat = false,
						 bBored = false;
					do
					{
						auto pArgLinkPtr = s_ArgPtrs.Begin();

						// Go through all the overloads, remove any incompatible, switch to better matches, exit when only one overload remains
						for(auto pCmdLink = s_Commands.Begin(); pCmdLink != s_Commands.Bad() && pArgLinkPtr != s_ArgPtrs.Bad(); ++pCmdLink, ++pArgLinkPtr)
						{
							auto	pArgLink = *pArgLinkPtr;
							bool	bIsCompatible = false,
									bIsBasicallyCompatible = false,
									bIsStrictlyCompatible = false;
							int		nMatchLevel = 0;

							if(pArgLink != pCmdLink->GetArgs()->Bad())
							{
								// Do we meet our own standards?
								switch(SCRambl.Config.Parser.GetTypeMatchRestriction())
								{
								case MATCH_RESTRICTION_STRICT:
									if(pArgLink->IsStrictlyCompatible(pToken->GetArgType()))
									{
										bIsStrictlyCompatible = true;
										bIsBasicallyCompatible = true;
										bIsCompatible = true;
									}
									break;
								case MATCH_RESTRICTION_BASIC:
									if(pArgLink->IsStrictlyCompatible(pToken->GetArgType()))
									{
										bIsCompatible = true;
										bIsBasicallyCompatible = true;
										bIsStrictlyCompatible = true;
									}
									else if(pArgLink->IsCompatible(pToken->GetArgType()))
									{
										bIsCompatible = true;
										bIsBasicallyCompatible = true;
									}
									break;
								case MATCH_RESTRICTION_NONE:
									bIsCompatible = true;
									if(pArgLink->IsStrictlyCompatible(pToken->GetArgType()))
									{
										bIsBasicallyCompatible = true;
										bIsStrictlyCompatible = true;
									}
									else if(pArgLink->IsCompatible(pToken->GetArgType()))
									{
										bIsBasicallyCompatible = true;
									}
									break;
								}
								if(bIsCompatible)
								{
									++nMatchLevel;
									if(bIsBasicallyCompatible) ++nMatchLevel;
									if(bIsStrictlyCompatible) ++nMatchLevel;
								}
								if(!bIsCompatible || nMatchLevel < nBestMatchLevel)
								{
									s_ArgPtrs.Drop(pArgLinkPtr);
									s_Commands.Drop(pCmdLink);
									if(--nNumOverloads <= 1)
									{
										bOverloaded = false;
										break;
									}
								}
								else if(nMatchLevel > nBestMatchLevel)
								{
									// It's kinda like we're a train changing tracks here...
									pCommand = *pCmdLink;
									pArgs = pCommand->GetArgs();
									pLink = pArgLink;
									bRepeat = nBestMatchLevel > 0;
									nBestMatchLevel = nMatchLevel;
									bBored = false;						// well, it is kinda exciting...
									++pArgLink;
									if(!bRepeat) (*pArgLinkPtr)++;
								}
								else
								{
									if(bBored) bRepeat = false;

									// all other passing commands still need arg ptrs incrementing
									++pArgLink;
									if(!bRepeat) (*pArgLinkPtr)++;
								}
							}
							else
							{
								s_ArgPtrs.Drop(pArgLinkPtr);
								s_Commands.Drop(pCmdLink);
								if(--nNumOverloads <= 1)
								{
									bOverloaded = false;
									break;
								}
							}
						}
						if(!bOverloaded)
						{
							pCommand = *s_Commands.Begin();
							pArgs = pCommand->GetArgs();
							pArgLinkPtr = s_ArgPtrs.Begin();
							auto pArgLink = *pArgLinkPtr;
							pLink = pArgLink;
							bRepeat = false;
						}

						// In case the last param we checked was a better match than previous ones,
						// we want to go back and check if the others are 
						if(bRepeat) bBored = true;
					}
					while(bRepeat);
				}
			}

			if(pToken && pToken != s_pEOALToken)
			{
				// we might want to convert the tokens type to match the command argument, unless it's less informative than the current type
				if(bIsCommand && pArg->GetType() && !pArg->IsAmbiguous())
				{
					ConvertTokenType(pToken, pArg->GetType());
				}
				TranslateToken(pToken);
			}
			if(TokenType == TOKEN_NONE)
			{
				if(pLink != pArgs->Bad() && pLink->IsVarArgs()) ++pLink;
				else break;
			}
			else if(pLink != pArgs->Bad())
			{
				if(pLink->IsNumArgs())
				{
					if(!bInNumArgs)
					{
						if(nNumArgsLeft = pToken->GetIntValue())
						{
							bInNumArgs = true;
							pToken = RegisterToken(TOKEN_VARARG_NUM_ARGS);
							pToken->SetValue(nNumArgsLeft);
						}
					}
					else if(--nNumArgsLeft <= 0)
						bInNumArgs = false;
				}
				if(!pLink->IsVarArgs() && !bInNumArgs) ++pLink;
			}
		}

		if(bOverloaded)
			throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1086"), NULL, 0, "%s", pCommandToken->GetString()));
	}

	if(bStartFresh)
	{
		SetToken(s_nBaseVarTokenIdx, nullptr);
		s_nNumRegisteredTokens = s_nBaseVarTokenIdx;
	}
	if(bIsCommand)
	{
		pCommandToken->SetValue(pCommand);
	}

	if(s_bLogicalOperatorNot) pCommandToken->SetConstruct(SCR_NOT);
	
	if(bIsCommand)
	{
		if(!pCommand->IsConditional())
		{
			if(s_nLastParseID == SCRIPT_IF) throw(SCRambl.GetText("ERRORS", "E1087"));
			else if(s_bLogicalOperatorNot && !pCommand->IsConditional()) throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1088"), NULL, 0, "%s", "NOT"));
			else if(s_bLogicalOperator != -1) throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1088"), NULL, 0, "%s", s_bLogicalOperator == LOGICAL_AND ? "AND" : "OR"));
		}
		else if(s_bLogicalOperatorNot) s_bLogicalOperatorNot = false;
	}
	else
	{
		if(!pOperation->IsConditional())
		{
			if(s_nLastParseID == SCRIPT_IF) throw("operation is not conditional");
			else if(s_bLogicalOperatorNot) throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1088"), NULL, 0, "%s", "NOT"));
			else if(s_bLogicalOperator != -1) throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1088"), NULL, 0, "%s", s_bLogicalOperator == LOGICAL_AND ? "AND" : "OR"));
		}
		else if(s_bLogicalOperatorNot) s_bLogicalOperatorNot = false;
	}
}
CToken * CParser::ProcessLabel(bool bCreate, bool bForceNew)
{
	CToken * pToken;

	// We've got the label pointer? The label has already been added (duplicate label declarations already handled by now)
	if(s_pLabel && s_bLabelIsDefined)
	{
		if(bCreate && bForceNew) throw(SCRambl.GetText("ERRORS", "E1089"));

		// If we're feeding from a macro, the token is already added
		if(!s_bWeAreBeingFedByMacro)
			pToken = RegisterToken(TOKEN_LABEL, s_pCurrentToken);
		else
			pToken = s_pToken;

		pToken->SetValue(s_pLabel);
		pToken->SetValueType(CSyntax::GetExtendedType(ET_LABEL));

		// Some labels may never even get referenced (we might need to know that in the future)
		s_pLabel->IsReferenced(true);
	}
	else
	{
		if(bCreate)
		{
			if(s_pLabel)
			{
				s_pLabel->SetLineNum(s_nNumCodeLines);
				s_pLabel->WaitingForDefinition(false);
			}
			else s_pLabel = CLabels::Add(s_pCurrentToken, s_nNumCodeLines);
			
			s_pLabel->IsHex(s_bIsInHexStruct);
			s_pLabel->InAsm(s_bIsInAsmStruct);
		}
		else
		{
			// It should be a label pointer, but the label isn't defined yet. So create one and mark it as needing a definition
			s_pLabel = CLabels::Add(s_pCurrentToken, s_nNumCodeLines);
			s_pLabel->WaitingForDefinition(true);
			s_pLabel->IsReferenced(true);
			s_pScript->Add(s_pLabel);

			pToken = RegisterToken(TOKEN_LABEL, s_pCurrentToken);
			pToken->SetValue(s_pLabel);
			pToken->SetValueType(CSyntax::GetExtendedType(ET_LABEL));
			return pToken;
		}

		// Register this, but skip it in compilation and offset calculation as it is just a declaration, after all
		s_pScript->Add(s_pLabel);
		pToken = RegisterToken(TOKEN_LABEL, s_pCurrentToken);
		pToken->SetValue(s_pLabel);
		pToken->IsSkippedDuringCompilation(true);
		NextLine();

		// Are there any label pointers we weren't sure about?
		auto pLabelToken = s_MaybeLabelTokens.Get(s_pLabel->GetHash());
		while(pLabelToken && !s_pLabel->IsNamed(pLabelToken->GetString()))
			pLabelToken = s_MaybeLabelTokens.Next();
		while(pLabelToken)
		{
			if(pLabelToken->GetType() == TOKEN_HEXMAYBELABEL)
			{
				pLabelToken->SetType(TOKEN_LABEL);
				pLabelToken->SetValue(s_pLabel);
				s_pLabel->IsReferenced(true);
			}
			do pLabelToken = s_MaybeLabelTokens.Next();
			while(pLabelToken && !s_pLabel->IsNamed(pLabelToken->GetString()));
		}
	}
	return pToken;
}
CToken * CParser::ProcessValue()
{
	auto pToken = RegisterToken(TOKEN_VALUE, s_pCurrentToken);
	if(IsStringInt(s_pCurrentToken))
	{
		int val = StringToInt(s_pCurrentToken);
		pToken->SetValue(val);
		pToken->SetValueType(CSyntax::GetType(TYPE_INT));
	}
	else if(IsStringFloat(s_pCurrentToken))
	{
		pToken->SetValue(StringToFloat(s_pCurrentToken));
		pToken->SetValueType(CSyntax::GetType(TYPE_FLOAT));
	}
	else throw(SCRambl.GetText("ERRORS", "E1060"));
	return pToken;
}
CToken * CParser::ProcessVariable()
{
	CToken * pToken;

	// Fetch the array index token - which could be a constant index or a variable index
	const char * pStr = s_pCurrentToken;
	if(pToken = FetchArrayIndex())
	{
		// Is the var magically casted?
		if(s_pCast->IsActive())
		{
			auto pVarToken = ProcessCast(pStr);
			/*pVarToken->SetValue(StringToInt(s_pCurrentToken));
			pVarToken->SetValueType(s_pCast->GetDestType());*/
			pVarToken->SetSubToken(pToken);
			pToken = pVarToken;
			AddToken(pToken);
		}
		// If a variable index was used, register a token for the var and set the var index as the sub-token
		else if(!s_bInConsoleMode)
		{
			auto pVarToken = RegisterToken(TOKEN_VAR, s_pCurrentToken);
			pVarToken->SetValue(s_pVariable);
			pVarToken->SetValueType(s_pVariable->GetType());
			pVarToken->SetSubToken(pToken);
			pToken = pVarToken;
		}
		else AddToken(pToken);
	}
	else
	{
		if(s_pCast->IsActive())
		{
			pToken = ProcessCast();
			AddToken(pToken);
		}
		else if(s_pVariable->IsArray())
		{
			// Array variable being used alone
			pToken = RegisterToken(TOKEN_VARINDEX, s_pCurrentToken);
			pToken->SetValue(s_pVariable->GetIndex());
			pToken->SetValueType(s_pVariable->GetType());
		}
		else
		{
			// Standard ol' variable
			pToken = RegisterToken(TOKEN_VAR, s_pCurrentToken);
			pToken->SetValue(s_pVariable);
			if(s_bCastMagicActivated)
			{
				pToken->SetValueType(s_pCast->GetDestType());
			}
			else pToken->SetValueType(s_pVariable->GetType());
		}
	}
	return pToken;
}
CToken * CParser::ProcessString()
{
	CToken * pToken = nullptr;
	if(s_bIsInConstStruct && !s_bIsInAsmStruct)
	{
		TODO("Redo #const...#endconst");
		const char * szName = s_pCurrentToken;

		if(GetTokenData() == TOKEN_TYPENAME)
		{
			if(!(s_pDeclaredVarType->GetBasicType() & PT_ANYBASIC)) throw(SCRambl.GetText("ERRORS", "E1090"));

			switch(GetTokenData())
			{
			case TOKEN_VALUE:
				if(IsStringInt(s_pCurrentToken))
					auto pConstant = CConstants::AddForScript<int>(szName, StringToInt(s_pCurrentToken), PT_INT);
				else if(IsStringFloat(s_pCurrentToken))
					auto pConstant = CConstants::AddForScript<float>(szName, (float)StringToFloat(s_pCurrentToken), PT_FLOAT);
				
				break;
			case TOKEN_STRING:
			case TOKEN_TYPENAME:
				//pConstant->CopyString(s_pCurrentToken);
				break;
			case TOKEN_LABEL:
				//pConstant->SetValue(s_pLabel);
				break;
			}
		}
		else throw(SCRambl.GetText("ERRORS", "E1091"));
	}
	else
	{
		pToken = RegisterToken(TOKEN_STRING, s_pCurrentToken);
		if(pToken->GetStringLength() < CLimits::GetMaxTextLabelLength())
			pToken->SetValueType(CSyntax::GetType(TYPE_TEXT_LABEL));
		else
			pToken->SetValueType(CSyntax::GetType(TYPE_STRING));
	}
	return pToken;
}
CToken * CParser::ProcessCast(const char * pStr)
{
	if(!s_pCast->IsActive()) return nullptr;

	// castaway!
	CToken * pToken;
	if(!pStr) pStr = s_pCurrentToken;
	//s_bCastMagicActivated = false;
	
	// cast to?
	switch(s_pCast->GetDestType()->GetTokenType())
	{
	case TOKEN_VALUE:
		// cast from?
		pToken = new CToken(TOKEN_VALUE, pStr);
		switch(s_pCast->GetTokenType())
		{
		case TOKEN_STRING:
		case TOKEN_VALUE:
			if(IsStringInt(pStr))
				pToken->SetValue(StringToInt(pStr));
			else if(IsStringFloat(pStr))
				pToken->SetValue(StringToFloat(pStr));
			else if(s_pCast->GetTokenType() == TOKEN_STRING)
			{
				if(auto pConst = CConstants::Get(pStr))
					pToken->SetValue(pConst->GetValue());
				else
					throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1092"), NULL, 0, pStr));
			}
			else throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1093"), NULL, 0, pStr));
			break;
		case TOKEN_VAR:
			pToken->SetValue(s_pVariable->GetIndex());
			break;
		default:
			delete pToken;
			throw(SCRambl.GetText("ERRORS", "E1094"));
		}
		break;
	case TOKEN_VAR:
		// cast from?
		switch(s_pCast->GetTokenType())
		{
		case TOKEN_VALUE:
		case TOKEN_STRING:
			if(IsStringInt(pStr))
			{
				// cast an integer to a variable index like 'lvar_int:32'
				pToken = new CToken(TOKEN_VARINDEX, pStr);
				pToken->SetValue(StringToInt(pStr));
			}
			else if(s_pCast->GetTokenType() == TOKEN_STRING)
			{
				if(auto pConst = CConstants::Get(pStr))
				{
					pToken = new CToken(TOKEN_VARINDEX, pStr);
					pToken->SetValue(pConst->GetValue());
				}
				else throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1095"), NULL, 0, pStr));
			}
			else throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1096"), NULL, 0, pStr));
			break;
		case TOKEN_LABEL:
			// cast a label offset to variable index? sure... 'lvar_int:label'
			pToken = new CToken(TOKEN_LABEL, pStr);
			pToken->SetValue(s_pLabel);
			break;
		case TOKEN_COMMAND:
			// cast a command id to variable index? we're just working with any int values here...
			pToken = new CToken(TOKEN_VARINDEX, pStr);
			pToken->SetValue(s_pCommand->GetId());
		case TOKEN_VAR:
			break;
		default:
			throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1097"), NULL, 0, pStr, s_pCastedMagicType->GetName()));
		}
		break;
	case TOKEN_LABEL:
		// cast from?
		switch(s_pCast->GetTokenType())
		{
		case TOKEN_VALUE:
		case TOKEN_STRING:
			if(IsStringInt(pStr))
			{
				pToken = new CToken(TOKEN_VALUE, pStr);
				pToken->SetValue(StringToInt(pStr));
			}
			else if(s_pCast->GetTokenType() == TOKEN_STRING)
			{
				if(auto pConst = CConstants::Get(pStr))
				{
					pToken = new CToken(TOKEN_VARINDEX, pStr);
					pToken->SetValue(pConst->GetValue());
				}
				else throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1098"), NULL, 0, "%s", pStr));
			}
			else throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1099"), NULL, 0, "%s", pStr));
			break;
		case TOKEN_VAR:
			pToken = new CToken(TOKEN_VALUE, pStr);
			pToken->SetValue(s_pVariable->GetIndex());
			break;
		case TOKEN_LABEL:
			// what a waste of time
			pToken = new CToken(TOKEN_LABEL, pStr);
			pToken->SetValue(s_pLabel);
			break;
		}
		break;
	}
	pToken->SetValueType(s_pCast->GetDestType());
	pToken->IsCasted(true);
	return pToken;
}

void CParser::ParseSwitchBegin()
{
	auto		pCommand = GetParseCommand(SCRIPT_CASE),
				pCaseCmd = GetParseCommand(SCRIPT_WHEN);
	CToken	*	pToken;
	
	// Add this control structure
	CreateControlStructCASE();

	// Check input
	if(GetTokenData() != TOKEN_VAR) throw(SCRambl.GetText("ERRORS", "E1071"));
	if(!s_pVariable->GetType()->IsInt()) throw(SCRambl.GetText("ERRORS", "E1100"));

	// (CASE) VAR
	pToken = RegisterToken(TOKEN_COMMAND, pCommand->GetName());
	pToken->SetValue(pCommand);
	
	// CASE (VAR)
	pToken = RegisterToken(TOKEN_VAR, s_pVariable->GetName());
	pToken->SetValue(s_pVariable);
	pToken->SetValueType(s_pVariable->GetType());
	TranslateToken(pToken);
	
	// (num cases)
	pToken = RegisterToken(TOKEN_VALUE);
	pToken->SetValueType(CSyntax::GetType(TYPE_INT));
	pToken->SetValue(0);
	s_pControlStructure->SetToken(pToken, 0);

	// Create a label for the default case of the switch
	sprintf(g_szTempString, "__SPECIAL_LABEL_CASE_ELSES_%d", s_nNumIFs);
	s_pLabel = CLabels::Add(g_szTempString, s_nNumCodeLines);
	s_pLabel->SetTokenLine(s_nNumLinesParsed);
	s_pControlStructure->SetBeginLabel(s_pLabel);

	// Create a label for the end of the switch
	sprintf(g_szTempString, "__SPECIAL_LABEL_CASE_END_%d", s_nNumIFs);
	s_pLabel = CLabels::Add(g_szTempString, s_nNumCodeLines);
	s_pControlStructure->SetEndLabel(s_pLabel);
	++s_nNumIFs;

	auto pType = CSyntax::GetExtendedType(ET_LABEL);
	
	// Get number of label args in switch commands
	int n1 = GetParseCommand(SCRIPT_CASE)->GetNumArgsOfType(pType) - 1,
		n2 = GetParseCommand(SCRIPT_WHEN)->GetNumArgsOfType(pType),
		n = (int)ceil((float)(CLimits::GetMaxSwitchSize() - n1) / (float)n2),
		nParams = n1 + 1;

	bool bDoneFirstCommand = false;
	
	if(n && nParams)
	{
		//PushCodePosition();
		
		// n == number of lines needed for the max number of switch cases
		do
		{
			if(bDoneFirstCommand)
			{
				pToken = RegisterToken(TOKEN_COMMAND, pCaseCmd->GetName());
				pToken->SetValue(pCaseCmd);
			}

			for(int i = 0; i < nParams; ++i)
			{
				pToken = RegisterToken(TOKEN_VALUE, "-1");
				pToken->SetValueType(CSyntax::GetType(TYPE_INT));
				pToken->SetValue(-1);

				pToken = RegisterToken(TOKEN_LABEL, s_pControlStructure->GetEndLabel()->GetName());
				pToken->SetValueType(CSyntax::GetExtendedType(ET_LABEL));
				pToken->SetValue(s_pControlStructure->GetEndLabel());
			}

			if(!bDoneFirstCommand)
			{
				bDoneFirstCommand = true;
				nParams = n2 + 1;
			}
			NextLine();
		}
		while(n--);

		//PopCodePosition();
	}
	else throw(SCRambl.GetText("ERRORS", "E1101"));
}
void CParser::ParseCaseBegin()
{
	if(s_pControlStructure->GetConstruct() != SCR_CASE)
		throw(SCRambl.GetText("ERRORS", "E1102"));
	if(GetTokenData() != TOKEN_VALUE)
		throw(SCRambl.GetText("ERRORS", "E1103"));
	if(!IsStringInt(s_pCurrentToken))
		throw(SCRambl.GetText("ERRORS", "E1103"));
	if(s_nNumSwitchCases > CLimits::GetMaxSwitchSize())
		throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1104"), NULL, 0, "%d", CLimits::GetMaxSwitchSize()));

	if(s_nNumSwitchCases)
	{
		// Create the jump-to-end code
		auto pToken = RegisterToken(TOKEN_COMMAND, s_pCurrentToken);
		pToken->SetValue(GetParseCommand(SCRIPT_GOTO));
		pToken = RegisterToken(TOKEN_LABEL);
		pToken->SetValue(s_pControlStructure->GetEndLabel());
		pToken->SetValueType(CSyntax::GetExtendedType(ET_LABEL));
		TranslateToken(pToken);
		NextLine();
	}

	int nNumCases = s_pControlStructure->GetToken(0)->GetIntValue(),
		line = 0,
		i = 0,
		value = StringToInt(s_pCurrentToken);

	auto pType = CSyntax::GetExtendedType(ET_LABEL);
	
	int caseParams = GetParseCommand(SCRIPT_WHEN)->GetNumArgsOfType(pType),
		switchParams = GetParseCommand(SCRIPT_CASE)->GetNumArgsOfType(pType) - 1;

	// Figure out where the token we need to update is...
	if(s_nNumSwitchCases < switchParams)
	{
		// we're on the initial switch command. we need to skip the command, var, num cases, default
		// value and default label and then go to the nth case params
		i = s_nNumSwitchCases * 2 + 5;
	}
	else
	{
		++line;
		for(int n=s_nNumSwitchCases - switchParams; n >= caseParams; n -= caseParams)
			++line;

		// here we only need to skip the command, but also need to keep s_nNumSwitchCases in range
		i = ((s_nNumSwitchCases - 7) % caseParams) * 2 + 1;
	}

	line += s_pControlStructure->GetLineDeclared();

	// set the case value
	auto pToken = GetToken(line, i);
	pToken->SetValue(value);
	pToken->SetString(s_pCurrentToken);

	// set the case label
	sprintf(g_szTempString, "__SPECIAL_LABEL_CASE_%d_%d", s_nNumIFs, s_nNumSwitchCases++);
	auto pLabel = CLabels::Add(g_szTempString, s_nNumCodeLines);
	pToken = GetToken(line, i + 1);
	pToken->SetValue(pLabel);
	pToken->SetString(g_szTempString);

	pToken = RegisterToken(TOKEN_LABEL);
	pToken->SetValue(pLabel);
	pLabel->IsInternal(true);
	pToken->IsSkippedDuringCompilation(true);
	NextLine();
}
void CParser::ParseSwitchEnd()
{
	auto pt = CSyntax::GetType(TYPE_INT);

	// Get number of cases in initial JT command, then the number in the extending command
	int n1 = GetParseCommand(SCRIPT_CASE)->GetNumArgsOfType(pt) - 1,
		n2 = GetParseCommand(SCRIPT_WHEN)->GetNumArgsOfType(pt);

	// Get the max number of 'cases' commands
	int l = 1 + (int)ceil((float)(CLimits::GetMaxSwitchSize() - n1 + 2) / (float)n2);

	// Get the number of needed 'cases' commands
	int n = (int)ceil((float)(s_nNumSwitchCases - n1) / (float)n2);

	// Set the total number of cases in the initial JT command
	s_pControlStructure->GetToken(0)->SetValue(s_nNumSwitchCases);

	// Cycle through the mass of jump table lines we've created
	for(int i=0; i<l; i++)
	{
		// Delete the ones we dont need
		if(i>n)
			ClearTokensOnLine(s_pControlStructure->GetLineDeclared() + i);
		else
		{
			// Translate all the ones we do need
			CToken * pToken;
			for(int j=0; pToken = GetToken(s_pControlStructure->GetLineDeclared() + i, j); ++j)
			{
				if(!pToken->IsSkippedDuringCompilation() && pToken->GetArgType())
					TranslateToken(pToken);
			}
		}
	}
		
	// GOTO... here
	s_pLabel = s_pControlStructure->GetEndLabel();
	auto pToken = RegisterToken(TOKEN_COMMAND);
	pToken->SetValue(GetParseCommand(SCRIPT_GOTO));
	pToken = RegisterToken(TOKEN_LABEL, s_pLabel->GetName());
	pToken->SetValue(s_pLabel);
	pToken->SetValueType(CSyntax::GetExtendedType(ET_LABEL));
	TranslateToken(pToken);
	NextLine();

	// Place the end label here (for CorrectOffsets to correct the code position)
	pToken = RegisterToken(TOKEN_LABEL, s_pLabel->GetName());
	pToken->SetValue(s_pLabel);
	pToken->IsSkippedDuringCompilation(true);
	NextLine();

	// END
	CloseControlStructure();
	--s_nSwitchDepth;
}

eTokenType CParser::GetTokenData(eTokenType ePredictedType)
{
	// Much prettier than previous versions of this function

	if(s_bWeAreBeingFedByMacro && (!s_pMacro->GetNumParams() || s_nNumMacroParamsPassed >= s_pMacro->GetNumParams()))
	{
		// Are we at the end of a macro?
		while(s_nMacroTokenIdx >= s_pMacro->GetNumTokens())
		{
			if(s_pMacro->m_pInheritedByMacro)
			{
				// This macro was part of a bigger macro, get it and loop if we're at the end of that one too
				auto pMacro = s_pMacro->m_pInheritedByMacro;
				s_pMacro->m_pInheritedByMacro = nullptr;
				s_nMacroTokenIdx = s_pMacro->m_nInheritedMacroIdx;
				s_pMacro = pMacro;
			}
			else
			{
				// This macro is done
				s_nMacroTokenIdx = 0;
				s_bWeAreBeingFedByMacro = false;

				// Spooky
				return GetTokenData(ePredictedType);
			}
		}

		// Get the next token from the macro
		auto pToken = s_pMacro->GetToken(s_nMacroTokenIdx++);

		// Macro parameter? Get the argument passed instead
		if(pToken->GetType() == TOKEN_MACROPARAM)
		{
			pToken = s_pMacro->TakeArg(pToken->GetMacroParamValue());
		}
		// Another macro? Feed from that one
		else if(pToken->GetType() == TOKEN_MACRO && pToken->GetMacroValue()->IsDeclared())
		{
			pToken->GetMacroValue()->m_pInheritedByMacro = s_pMacro;
			pToken->GetMacroValue()->m_nInheritedMacroIdx = s_nMacroTokenIdx;
			s_nMacroTokenIdx = 0;
			s_pMacro = pToken->GetMacroValue();
			return GetTokenData(ePredictedType);
		}

		s_pCurrentToken = pToken->GetString();
		return GetTokenData(pToken->GetString(), pToken->GetType() == TOKEN_STRING || ePredictedType ? ePredictedType : pToken->GetType());

		#if FALSE
		// A string will be used if, for example, the macro token is a variable which is not yet declared
		else if(pToken->GetType() == TOKEN_STRING)
		{
			if(!s_bIsInAsmStruct)
			{
				// Run the usual string->identifier procedure
				ttype = StoreIdentifierValue(pToken->GetString());

				// Is this possibly a var array?
				if(ttype == TOKEN_STRING && strchr(pToken->GetString(), SYMBOL_ARRAY_LBRACKET))
					ttype = TOKEN_VAR;
			}
			else ttype = TOKEN_STRING;
		}
		else
		{
			// Store info about the already defined (cache'd) token
			switch(ttype = pToken->GetType())
			{
			case TOKEN_LABEL:
				s_pLabel = pToken->GetLabelValue();
				s_bLabelIsDefined = !s_pLabel->WaitingForDefinition();
				break;
			case TOKEN_VAR:
				s_pVariable = pToken->GetVariableValue();
				break;
			case TOKEN_VARINDEX:
				//s_pVariable = pToken->GetIntValue();
				break;
			/*case TOKEN_OPERATOR:
				s_pOperator = pToken->GetOperatorValue();
				break;*/
			case TOKEN_COMMAND:
				if(!s_bIsInAsmStruct)
				{
					s_pCommand = pToken->GetCommandValue();
					s_pToken = pToken;
					return TOKEN_COMMAND;
				}
			}
		}

		CToken *pMacroToken;

		switch(ttype)
		{
		case TOKEN_VALUE:
			if(s_bIsInAsmStruct)
			{
				s_pToken = pToken;
				return TOKEN_STRING;
			}

			pMacroToken = pToken;
			pToken = RegisterToken(TOKEN_VALUE, pToken->GetString());
			pToken->SetValueType(pMacroToken->GetValueType());
			if(pMacroToken->GetValueType() == PT_FLOAT)
				pToken->SetValue(pMacroToken->GetFloatValue());
			else
				pToken->SetValue(pMacroToken->GetIntValue());
			break;
		case TOKEN_CONSTRUCT:
			s_pToken = pToken;
			return s_bIsInAsmStruct ? TOKEN_STRING : TOKEN_CONSTRUCT;
		case TOKEN_LABEL:
			pToken = RegisterToken(TOKEN_LABEL, pToken->GetString());
			s_pToken = pToken;
			return s_bIsInAsmStruct ? TOKEN_STRING : TOKEN_LABEL;
		case TOKEN_VAR:
			pToken = RegisterToken(TOKEN_VAR, pToken->GetString());
			pToken->GetString(s_szIdentifier);
			s_pToken = pToken;
			return TOKEN_VAR;
		case TOKEN_COMMAND:
			pToken->SetValue(s_pCommand);
			s_pToken = pToken;
			return s_bIsInAsmStruct ? TOKEN_STRING : TOKEN_COMMAND;
		case TOKEN_TYPENAME:
			s_pTypeName = pToken->GetString();
			s_pToken = pToken;
			return s_bIsInAsmStruct ? TOKEN_STRING : TOKEN_TYPENAME;
		default:
			pToken = RegisterToken(ttype, pToken->GetString());
			pToken->SetValueType(PT_NONE);
		}

		// We've already done what needs to be done with this token, return the next
		return GetTokenData();
		#endif
	}

	return ReadToken() >= 0 ? GetTokenData(s_pLineCurrent, ePredictedType) : TOKEN_NONE;
}
eTokenType CParser::GetTokenData(const char *szCode, eTokenType ePredictedType)
{
	// Messy stuff from GetIdentifier came here - this function can gather code information for any string
	if(!szCode) szCode = s_pLineCurrent;
	else if(szCode > s_pLineCurrent && szCode < s_pLineEnd) s_pLineCurrent += (szCode - s_pLineCurrent);

	// We might wanna read it a couple more times...
	s_pCurrentToken = szCode;

	// Anti-magic
	s_pCast->Deactivate();

	if(s_bIsInPreprocessorDirective)
	{
	}

	// Do easy quick checks first, before moving onto identifiers
	if(!s_bIsInAsmStruct)
	{
		// A literal string?
		if(*szCode == STRCHAR_BEGIN)
		{
			++s_pCurrentToken;
			++s_pLineCurrent;
			return TOKEN_STRING;
		}
		
		// Values are pretty easy...
		if(IsStringInt(szCode) || IsStringFloat(szCode))
		{
			if(szCode > s_pLineBegin && szCode < s_pLineEnd)
			{
				while(*s_pLineCurrent || IsSpace(*s_pLineCurrent))
					++s_pLineCurrent;
				++s_pLineCurrent;
			}
			return s_bIsInHexStruct && IsStringHex(szCode) ? TOKEN_HEX : TOKEN_VALUE;
		}
	}

	// We got a prediction for what's coming next?
	if(eTokenType ttype = StorePredictedIdentifier(szCode, ePredictedType))
	{
		s_ePredictedNextType = TOKEN_NONE;
		return ttype;
	}
		
	// Find first non-identifier char...
	while(IsIdentifierChar(*szCode) && *szCode != SYMBOL_ARRAY_LBRACKET)
	{
		if(s_pLineCurrent && *s_pLineCurrent == *szCode) ++s_pLineCurrent;
		++szCode;
	}

	// If we're in an inactive preprocessor struct, only continue for the sake of directives so we can check for #endif
	if(!s_bPreprocessorLogicHistory[s_nPreprocessorControlDepth] && *szCode != '#' && !s_bIsInPreprocessorDirective)
		return TOKEN_NONE;

	// What is it?
	switch(*szCode)
	{
	default:
		// Probably some kinda operator...
		if(s_bIsInAsmStruct) break;
		if(s_pLineCurrent)
		{
			while(*s_pLineCurrent || IsSpace(*s_pLineCurrent))
				++s_pLineCurrent;
			++s_pLineCurrent;
		}
		if(!(s_pOperator = COperators::Get(szCode)))
		{
			char op[MAX_OPERATOR_CHARS];
			strncpy(op, szCode, MAX_OPERATOR_CHARS);
			if(!(s_pOperator = COperators::Get(op)))
			{
				// might be a filename..
				if(*s_pLineCurrent != '.')
					throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1105"), NULL, 0, "%c", *szCode));
				else break;
			}
			return TOKEN_OPERATOR;
		}
		return TOKEN_OPERATOR;

	case PUNC_COMMA:
	//case PUNC_RBRACKET:
		// Comma gain?
		if(s_bIsInAsmStruct) break;
		if(s_bIsDeclaringMacro)
			return TOKEN_MACROPARAM;
		throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1105"), NULL, 0, "%c", *szCode));

	case SYMBOL_SCRAMBL_CLASS:
		if(s_bIsInAsmStruct) break;
		*s_pLineCurrent++ = '\0';
		++szCode;
		s_pToken = GetSCRamblToken(s_pLineCurrent);
		return TOKEN_HANDLED;

	case SYMBOL_DIRECTIVE:
		// Easy to spot a directive
		*s_pLineCurrent++ = '\0';
		s_bIsInPreprocessorDirective = true;
		++s_pCurrentToken;
		return TOKEN_DIRECTIVE;

	case SYMBOL_SCOPE_BEGIN:
		// One day these should be useful for function scopes :)
		if(s_bIsInAsmStruct) break;
		if(s_pScript) s_pScript->OpenScope(s_nNumCodeLines);
		*s_pLineCurrent++ = '\0';
		return TOKEN_NONE;

	case SYMBOL_SCOPE_END:
		// Close the door on the locals
		if(s_bIsInAsmStruct) break;
		if(s_pScript) s_pScript->CloseScope(s_nNumCodeLines);
		*s_pLineCurrent++ = '\0';
		return TOKEN_NONE;

	case SYMBOL_HIGHLIGHT_BEGIN: // these should only exist for preprocessor stuff
		if(s_bIsInAsmStruct) break;
		// Pre-process preprocessor stuff before processing further
		if(s_bIsDeclaringMacro) return TOKEN_STRING;
		else if(s_bInlinePreprocessing || s_bIsInPreprocessorDirective || s_bWeAreBeingFedByMacro)
		{
			// Evaluate integer constant expressions
			static char expr[4096];
			int v;
			if(s_bWeAreBeingFedByMacro)
			{
				strcpy(expr, s_pCurrentToken);
				auto pexpr = expr;
				v = EvaluateExpression(pexpr);
			}
			else
			{
				v = EvaluateExpression(s_pLineCurrent);
			}
			strcpy(expr, tostr(v));
			s_pCurrentToken = expr;
			return TOKEN_VALUE;
		}
		break;
			
	case SYMBOL_HIGHLIGHT_END:
	case SYMBOL_ARRAY_LBRACKET: // check for array'd variable
		if(s_bIsInAsmStruct) break;
		if(s_pLineCurrent != s_pCurrentToken)
		{
			if(!ePredictedType)
			{
				*s_pLineCurrent = '\0';
				auto r = StorePredictedIdentifier(s_pCurrentToken, TOKEN_VAR);
				*s_pLineCurrent = SYMBOL_ARRAY_LBRACKET;
				if(r) return r;
			}
		}
		else
		{
			// Inline expressions in SCR code? Yaaayyyy!
			if(s_pLineCurrent == szCode)
			{
				// we're only modify our lines
				*s_pLineCurrent++ = '\0';
				if(char * str = strrchr(s_pLineCurrent, ']'))
					*str = '\0';
			}
			s_bInlinePreprocessing = true;
			++szCode;
			eTokenType ttype;
			if(ttype = GetTokenData(szCode, ePredictedType))
			{
				if(ttype == TOKEN_MACRO && !s_bDontFeedFromMacro)
				{
					s_bWeAreBeingFedByMacro = true;
					s_nNumMacroParamsPassed = 0;
					s_nMacroTokenIdx = 0;
					s_bExpectAnything = true;
					return GetTokenData();
				}
			}
			return ttype;
		}
	case '\0': break;

	case SYMBOL_LABEL:
		*s_pLineCurrent++ = '\0';
		// (cast?)
		if(*s_pLineCurrent)
		{
			if(auto pType = CSyntax::GetTypeByName(s_pCurrentToken))
			{
				s_pLineTokens[s_nNumTokens-1] = s_pLineCurrent;
				s_pCast->Activate(pType, GetTokenData(s_pCurrentToken + pType->GetNameLength() + 1, pType->GetTokenType()));
				return pType->GetTokenType();
			}
			else throw(SCRambl.GetText("ERRORS", "E1094"));
		}
		// (label)
		else if((s_pLabel = s_pScript->FindLabel(s_pCurrentToken)))
		{
			if(s_pLabel->WaitingForDefinition()) s_bLabelIsDefined = false;
			else throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1106"), NULL, 0, "%s%d", s_pCurrentToken, s_pLabel->GetLineNum()));
		}
		else s_bLabelIsDefined = false;
		return TOKEN_LABEL;
	}

	// No symbol, THIS_IS_A_GENUINE_IDENTIFIER...
	return StoreIdentifierValue(s_pCurrentToken);
}
eTokenType CParser::StorePredictedIdentifier(const char *szIdentifier, eTokenType eType)
{
	if(eType == TOKEN_NONE || eType == TOKEN_MACRO)
	{
		if(auto pMacro = CMacros::Get(szIdentifier))
		{
			s_pMacro = pMacro;

			if(pMacro->IsDeclared())
			{
				if(pMacro->IsPreset())
				{
				}
				if(s_bIsInPreprocessorDirective || s_bInlinePreprocessing) return TOKEN_MACRO;
				if(!s_bIsDeclaringMacro && !s_bDontFeedFromMacro)
				{
					s_bWeAreBeingFedByMacro = true;
					s_nNumMacroParamsPassed = 0;
					s_nMacroTokenIdx = 0;
					s_bExpectAnything = true;
					return GetTokenData();
				}
				else return TOKEN_MACRO;
			}
		}
		if(eType == TOKEN_MACRO) return TOKEN_NONE;
	}

	if(s_bIsDeclaringMacro && s_pDeclaredMacro)
	{
		if(eType == TOKEN_NONE || eType == TOKEN_MACROPARAM)
		{
			if(s_pMacroParam = s_pDeclaredMacro->GetParam(szIdentifier))
				return TOKEN_MACROPARAM;
		}
	}

	switch(eType != TOKEN_NONE ? (eType != s_ePredictedNextType ? eType : TOKEN_NONE) : s_ePredictedNextType)
	{
	case TOKEN_CONSTRUCT:
		if((s_nConstructID = CSyntax::GetConstruct(szIdentifier)) != -1)
			return TOKEN_CONSTRUCT;
		if(eType == TOKEN_CONSTRUCT) break;
	case TOKEN_COMMAND:
		if(s_pCommand = CCommands::GetByName(szIdentifier))
			return TOKEN_COMMAND;
		if(s_ePredictedNextType != TOKEN_CONSTRUCT)
			break;
	case TOKEN_CONSTANT:
		
		break;
	case TOKEN_VAR:
		// predefined variables take precedence here
		if(s_pVariable = CVariables::GetByName(szIdentifier))
			return TOKEN_VAR;
		if(s_pVariable = s_pScript->FindVariable(szIdentifier))
			return TOKEN_VAR;
		break;
	case TOKEN_LABEL:
		if(s_pLabel = s_pScript->FindLabel(szIdentifier))
		{
			s_bLabelIsDefined = true;
			return TOKEN_LABEL;
		}
		break;
	case TOKEN_TYPENAME:
		if(s_pDeclaredVarType = CSyntax::GetTypeByName(szIdentifier))
			return TOKEN_TYPENAME;
		break;
	case TOKEN_STRING:	// (return whatever code with no lookups)
		return TOKEN_STRING;
	}
	return TOKEN_NONE;
}
eTokenType CParser::StoreIdentifierValue(const char *szIdentifier)
{
	if(auto eMacroTokenType = StorePredictedIdentifier(szIdentifier, TOKEN_MACRO))
		return eMacroTokenType;

	if(s_bIsInAsmStruct)
	{
		if(StorePredictedIdentifier(szIdentifier, TOKEN_VAR))
			return TOKEN_VAR;

		// Need to fix labels and add here

		return TOKEN_STRING;
	}

	if(!s_bIsInHexStruct)
	{
		if(StorePredictedIdentifier(szIdentifier, TOKEN_CONSTRUCT))
			return TOKEN_CONSTRUCT;
	}
	
	if(StorePredictedIdentifier(szIdentifier, TOKEN_TYPENAME))
		return TOKEN_TYPENAME;
	if(StorePredictedIdentifier(szIdentifier, TOKEN_COMMAND))
		return TOKEN_COMMAND;
	if(StorePredictedIdentifier(szIdentifier, TOKEN_VAR))
		return TOKEN_VAR;
	if(StorePredictedIdentifier(szIdentifier, TOKEN_LABEL))
		return TOKEN_LABEL;
	return s_bIsInHexStruct ? TOKEN_HEX : TOKEN_STRING;
}

void CParser::ParseLine()
{
	CToken	*	pToken		= RecallToken(0),
			*	pNotToken	= nullptr,
			*	pLogToken	= nullptr;

	// We'll skip the first token later (command token is passed to ParseCommand)
	int			nParam		= 1;

	// Check for AND/OR
	if(pToken->GetType() == TOKEN_CONSTRUCT)
	{
		switch(pToken->GetConstruct())
		{
		case SCR_AND:
		case SCR_OR:
			pLogToken = pToken;
			pToken = RecallToken(nParam++);
			break;
		}
	}

	// Check for NOT
	if(pToken->GetType() == TOKEN_CONSTRUCT)
	{
		if(pToken->GetConstruct() != SCR_NOT)
			throw(SCRambl.GetText("ERRORS", "E1107"));
		pNotToken = pToken;
		pToken = RecallToken(nParam++);
	}

	// Parse logical operator
	if(pLogToken) ParseConstruct(pLogToken, pToken);
	
	// Process a command or math line
	switch(pToken->GetType())
	{
	case TOKEN_COMMAND:
		ParseCommand(pToken, pToken->GetCommandValue(), nParam);
		break;
	case TOKEN_OPERATOR:
	case TOKEN_VAR:
	case TOKEN_VARINDEX:
		// Dont skip the first non-construct token for math
		ParseMath(nParam - 1);
		break;
	default: throw("Dont know what to do with this token");
	}
}
void CParser::DoLineFinalPass()
{
	// New & Improved
	// Check for stuff we can optimise by evaluating now (const math expressions, etc.)
	if(s_bIsInPreprocessorDirective) return;
	int			i = 0,
				nEvalBeginTokenIdx,
				nEvaldVal,
				nOpChar,
				nEvalOpChar = 0;
	float		fEvaldVal;
	bool		bEvaluating = false,
				bWasEvaluating;
	CToken	*	pToken,
			*	pEvalCmdToken,
			*	pEvalVarToken = nullptr,
			*	pEvalValToken = nullptr;
	const CTypeBase	*	pEvalType;
	
	// Iterate through all the tokens on this line
	while(true)
	{
		bWasEvaluating = bEvaluating;

		if(pToken = RecallToken(i++))
		{
			// Optimise const math stuff
			if(pToken->GetType() == TOKEN_OPERATOR)
			{
				if((pEvalCmdToken = !bEvaluating || pToken->GetOperationValue() == pEvalCmdToken->GetOperationValue() ? pToken : nullptr))
				{
					pToken = RecallToken(i++);
					pEvalVarToken = !bEvaluating || pToken->GetVariableValue() == pEvalVarToken->GetVariableValue() ? pToken : nullptr;
			
					if(pEvalVarToken && pEvalVarToken->GetType() == TOKEN_VAR)
					{
						pEvalValToken = RecallToken(i++);
						pEvalType = pEvalValToken->GetArgType();

						if(pEvalType->IsInt() || pEvalType->IsFloat())
						{
							nOpChar = pEvalCmdToken->GetOperationValue()->GetOperator()->GetOperatorString()[0];

							if(bEvaluating)
							{
								if(nOpChar == nEvalOpChar)
								{
									switch(nOpChar)
									{
									case OPERATOR_ADD:
										if(pEvalType->IsInt()) nEvaldVal += pEvalValToken->GetIntValue();
										else fEvaldVal += pEvalValToken->GetFloatValue();
										break;
									case OPERATOR_SUBTRACT:
										if(pEvalType->IsInt()) nEvaldVal -= pEvalValToken->GetIntValue();
										else fEvaldVal -= pEvalValToken->GetFloatValue();
										break;
									case OPERATOR_MULTIPLY:
										if(pEvalType->IsInt()) nEvaldVal *= pEvalValToken->GetIntValue();
										else fEvaldVal *= pEvalValToken->GetFloatValue();
										break;
									case OPERATOR_DIVIDE:
										if(pEvalType->IsInt()) nEvaldVal += pEvalValToken->GetIntValue();
										else fEvaldVal += pEvalValToken->GetFloatValue();
										break;
									}
								}
								else bEvaluating = false;
							}
							else
							{
								switch(nOpChar)
								{
								case OPERATOR_ADD:
								case OPERATOR_DIVIDE:
								case OPERATOR_MULTIPLY:
								case OPERATOR_SUBTRACT:
									nEvalOpChar = nOpChar;
									if(pEvalType->IsInt()) nEvaldVal = pEvalValToken->GetIntValue();
									else fEvaldVal = pEvalValToken->GetFloatValue();
									bEvaluating = true;
									nEvalBeginTokenIdx = i - 1;
									break;
								}
							}
						}
						else bEvaluating = false;
					}
					else bEvaluating = false;
				}
				else bEvaluating = false;
			}
			else bEvaluating = false;
		}
		else bEvaluating = false;
		
		if(!bEvaluating && bWasEvaluating)
		{
			for(int j=nEvalBeginTokenIdx + 1; j<=i; ++j)
			{
				if(RecallToken(j))
					SetToken(j, s_pNullToken);
				else break;
			}
			if(pEvalType->IsInt()) RecallToken(nEvalBeginTokenIdx)->SetValue(nEvaldVal);
			else RecallToken(nEvalBeginTokenIdx)->SetValue(fEvaldVal);
		}

		if(!pToken) break;
	}

		/*switch(pToken->GetType())
		{
		case TOKEN_OPERATOR:
			
			// Cheaply get the primary operator char (if it's a # command, this hasn't been converted to an operation yet)
			nOpChar = s_bIsInPreprocessorDirective	? pToken->GetOperatorValue()->GetOperatorString()[0]
													: pToken->GetOperationValue()->GetOperator()->GetOperatorString()[0];
			
			// What can we do for this one?
			switch(nOpChar)
			{
			case OPERATOR_ADD:
			case OPERATOR_DIVIDE:
			case OPERATOR_MULTIPLY:
			case OPERATOR_SUBTRACT:
				// If we've not got a value to operate on, there's no use in the operator...
				if(bEvaluating)
				{
					//if(nEvalOpChar != nOpChar) goto FinishEvaluation;
					if(eEvalType == EVAL_TYPE_MATH)
					{
 						switch(nOpChar)
						{
						case OPERATOR_ADD:
						case OPERATOR_DIVIDE:
						case OPERATOR_MULTIPLY:
						case OPERATOR_SUBTRACT:
							nEvalOpChar = nOpChar;
							break;
						default:
							goto FinishEvaluation;
						}
					}

					nNumEvaldTokens++;
				}
				break;
			default:
				bEvaluating = false;
			}
			break;
		case TOKEN_VAR:
			if(!bEvaluating) pEvalVarToken = pToken;
			else if(pEvalVarToken != pToken) goto FinishEvaluation;		// OMG I HAVE SINNED! Chill out, k?
			break;
		case TOKEN_VALUE:
			if(bEvaluating && eEvalType == EVAL_TYPE_MATH)
			{
				if(pEvalBeginToken->GetValueType() == pToken->GetValueType())
				{
					switch(nEvalOpChar)
					{
					case OPERATOR_ADD:
						pToken->GetValueType() == PT_FLOAT ? fEvaldVal += pToken->GetFloatValue() : nEvaldVal += pToken->GetIntValue();
						break;
					case OPERATOR_DIVIDE:
						pToken->GetValueType() == PT_FLOAT ? fEvaldVal /= pToken->GetFloatValue() : nEvaldVal /= pToken->GetIntValue();
						break;
					case OPERATOR_MULTIPLY:
						pToken->GetValueType() == PT_FLOAT ? fEvaldVal *= pToken->GetFloatValue() : nEvaldVal *= pToken->GetIntValue();
						break;
					case OPERATOR_SUBTRACT:
						pToken->GetValueType() == PT_FLOAT ? fEvaldVal -= pToken->GetFloatValue() : nEvaldVal -= pToken->GetIntValue();
						break;
					}
					break;
				}
			}
			else
			{
				if(pToken->GetValueType() == PT_INT)
				{
					bTempType = PT_INT;
					nTempEval = 0;
					nEvaldVal = pToken->GetIntValue();
				}
				else if(pToken->GetValueType() == PT_FLOAT)
				{
					bTempType = PT_FLOAT;
					fTempEval = 0.0;
					fEvaldVal = pToken->GetFloatValue();
				}
				else break;

				bEvaluating = true;
				pEvalBeginToken = pToken;
				nEvalBeginTokenIdx = i;
				nNumEvaldTokens = 0;
				eEvalType = EVAL_TYPE_MATH;
				break;
			}
		default:
FinishEvaluation:
			if(bEvaluating)
			{
				bEvaluating = false;

				if(nNumEvaldTokens > 1)
				{
					CToken * pTmpToken;
					int n = nNumEvaldTokens + nEvalBeginTokenIdx;
				
					// Remove the tokens we've optimised out...
					for(int j=nEvalBeginTokenIdx; j<=n; j++)
					{
						if(pTmpToken = RecallToken(j))
							SetToken(j, s_pNullToken);
						else break;
					}

					// Set the resulting value
					pEvalBeginToken->GetValueType() == PT_FLOAT ? pEvalBeginToken->SetValue(fEvaldVal) : pEvalBeginToken->SetValue(nEvaldVal);

					// This token is actually skipped by the compiler, but we're using it as a dummy to continue evaluating
					//pTmpToken = RecallToken(n-1);
					//pTmpToken->SetValueType(pEvalBeginToken->GetValueType());
					//pEvalBeginToken->GetValueType() == PT_FLOAT ? pTmpToken->SetValue(fEvaldVal) : pTmpToken->SetValue(nEvaldVal);
				}
			}
		}
	}

	// Still evaluating? Finish it up...
	if(bEvaluating)
	{
		nNumEvaldTokens++;
		goto FinishEvaluation;
	}*/
 }

bool CParser::ParseMath(int TokenIdx)
{
	// Ah, the days we had to go to this much effort...
#if FALSE
	CToken		* pToken;

	CMathOperation MathOperation;

	// Outsource our job to CMathOperation, which will order and evaluate operations
	for(int i=TokenIdx; pToken = RecallToken(i); i++)
	{
		MathOperation.AddExpression(pToken);
		SetToken(i, nullptr);
	}

	MathOperation.Process(s_pTokens[s_nNumLinesParsed], s_nLastParseID == SCRIPT_IF && s_bLineContinuesConditions);

	// We've got the tokens stored in MathOperation, which will place them back in order...
	//ClearTokensOnLine(s_nNumLinesParsed-1);
#endif
	return true;
}
bool CParser::ParseConstruct(CToken *pToken, CToken *pOpToken)
{
	int nConstruct = 0;

	switch(nConstruct = pToken->GetConstruct())
	{
	case SCR_NOT:
		if(!pOpToken->GetOperationValue()->IsConditional())
		{
			if(pOpToken->GetType() == TOKEN_COMMAND || pOpToken->GetType() == TOKEN_OPERATOR)
				throw(SCRambl.GetText("ERRORS", "E1108"));
		}
		return true;
	case SCR_AND:
		if(s_bLogicalOperator == -1) s_bLogicalOperator = LOGICAL_AND;
		else if(s_bLogicalOperator != LOGICAL_AND)
			throw(SCRambl.GetText("ERRORS", "E1109"));
	case SCR_OR:
		if(nConstruct != SCR_AND)
		{
			if(s_bLogicalOperator == -1) s_bLogicalOperator = LOGICAL_OR;
			else if(s_bLogicalOperator != LOGICAL_OR) throw(SCRambl.GetText("ERRORS", "E1109"));
		}

		if(pOpToken->GetType() != TOKEN_OPERATOR && pOpToken->GetType() != TOKEN_VAR)
		{
			if(pOpToken->GetType() != TOKEN_COMMAND || !pOpToken->GetCommandValue()->IsConditional())
				throw(SCRambl.GetText("ERRORS", "E1110"));
		}
		return true;
	}
	return false;
}
void CParser::ParseCommand(CToken *pCommandToken, const CCommand *pCommand, int TokenIdx)
{
	// OLD
#if FALSE
	//CParamType			pt, at;
	CToken			*	pToken;
	int					nFirstTokenID;

	nFirstTokenID = TokenIdx;

	if(s_nLastParseID == SCRIPT_IF)
	{
		if(!pCommand->IsConditional()) throw("Expected condition");
		s_nNumConditions++;
	}
	if(pCommand->GetParseFunc() == -1)
	{
		while((pToken = RecallToken(TokenIdx)) && pToken->GetType() == TOKEN_CONSTRUCT)
			TokenIdx++;
	}

	// To find the best match for an overloaded command...
	auto pMostCompatibleCommand = pCommand;
	int nBestCompatibilityScore = 0,
		nParamBeginIdx = TokenIdx;

	// If the best overload has already been found or the command isn't overloaded, errors will be thrown for incompatible params
	bool bFoundBestOverload = !pCommand->IsOverloaded();

	// Get arg list
	auto args = pCommand->GetArgs();

	while(true)
	{
		TokenIdx = nParamBeginIdx;

		// Find the right overloaded command from the total compatibility of it's params
		int nCompatibilityScore = 0;
		const CTypeBase * pVarArgType = nullptr;
		bool bVarArgMode = false;
		int nVarArgLimit = 0;

		// This command even got any arguments? If not, no point in checking them
		if(pCommand->GetNumArgs())
		{
			int i = 0;

			// Iterate through the args - we will pause iteration for var-length args and wait until we've reached var arg limit
			// this could be a limit set in the commands.def file or use-specific limit 
			for(auto pLink = args->Begin(); pLink != args->Bad(); )
			{
				// 'Recall' a token from this line with a particular index
				pToken = RecallToken(TokenIdx++);

				// Are we (still) on a variable length argument?
				if(pLink->IsVarArgs())
				{
					// If we have a user-specified limit, stick to it
					if(nVarArgLimit > 0)
					{
						if(!bVarArgMode)
						{
							// Get the amount of args to process and possibly their type
							if(pLink->IsNumArgs())
							{
								if((!pToken || pToken->GetType() != TOKEN_VALUE) || !(nVarArgLimit = pToken->GetIntValue()))
									throw("Constant INT value needed specifying the amount of arguments to follow");
							}
							else if(pVarArgType = pLink->GetType()) nVarArgLimit = pLink->GetIndex();
							bVarArgMode = true;
						}
					}
					else
					{
						// Exit var arg mode and continue with the rest of the args (if any)
						pVarArgType = nullptr;
						bVarArgMode = false;
					}
				}

				// Get stuff ready for our (possibly strict) type-checking
				auto	pType = bVarArgMode ? pVarArgType : pLink->GetType();
				auto	pExtendedType = pType ? pType->Extend() : nullptr;
				int		nParamCompatibilityScore = 0;
				bool	bTokenParsed = false;

				// If we're not in "any amount of args" mode, check that we still have a token
				if(!bVarArgMode)
				{
					// Have we run out of tokens for our arguments? Bad news...
					if(pType)
					{
						if(!pToken)
						{
							if(pLink->IsReturn()) THROW("Destination needed for return of '%s'", pType->GetName());
							else THROW("Expected parameter for '%s' arg", pType->GetName());
						}
						//if(pType->IsPseudo()) pType = pType->Standardise();
					}
					else if(!pToken) pLink->IsReturn()	? throw("Destination needed for return")
														: throw("Expected parameter for arg");
				}
				else if(!pToken) throw("Expected argument (still in var arg list)");

				if(!pType)
				{
					// No specified type for this arg in commands.def, probably an unknown arg.. so predict a type based on the token
					/*auto idx = CSyntax::GetBasicTypeIndex(pToken->GetValueType());
					
					switch(SCRambl.Config.Parser.GetTypeMatchRestriction())
					{
					case MATCH_RESTRICTION_BASIC:
					default:
						if(!(pType = CSyntax::GetType(idx)))
							throw("Couldn't find basic type match for parameter with unknown arg type");
						break;
					case MATCH_RESTRICTION_STRICT:
						if(!(pType = CSyntax::GetTypeStrict(pt)))
							throw("Couldn't find strict type match for parameter with unknown arg type");
						break;
					}*/

					//pToken->SetValueType(pType);
				}
				
				// For most args (particularly those with extended types), perform any necessary conversions or checks with the token
				ProcessCommandArg(pToken, *pLink);

				// If we've got a return, we need to check that strings are actually strings before parsing, if not, do it after
				if(pToken->GetType() == TOKEN_STRING)
					ValidateStringToken(pToken, pType->Standardise(), pCommandToken, i);

				// If the argument is to be parsed, we better not let anyone down
				if(pLink->IsParsed() && ParseCommandToken(pCommandToken, pToken, pType->Standardise(), TokenIdx))
				{
					// We parsed this argument ourselves, so we know it's good...
					bTokenParsed = true;
					nParamCompatibilityScore += 3;
				}
				else
				{
					// Is there something dodgy about this arg? Ambiguity? Skip checking and allow just about anything here
					if(!pLink->IsAmbiguous() && !pCommand->IsAmbiguous())
					{
						// Build up the argument type and get the compatibility level
						auto pValType = pToken->GetArgType();
						bool bBasicMatch, bStrictMatch;

						if(bBasicMatch = pLink->IsCompatible(pValType ? pValType : pToken->GetArgType()))
						{
							++nParamCompatibilityScore;
							if(bStrictMatch = pLink->IsStrictlyCompatible(pValType ? pValType : pToken->GetArgType()))
								++nParamCompatibilityScore;
						}
						if(bFoundBestOverload)
						{
							// Not good enough? Dead end, bro...
							auto	expected = pExtendedType ? pExtendedType->GetName() : pType->GetName(),
									found = pToken->GetArgType() ? pToken->GetArgType()->GetName() : "UNKNOWN";

							switch(SCRambl.Config.Parser.GetTypeMatchRestriction())
							{
							case MATCH_RESTRICTION_STRICT:
								if(!bStrictMatch)
									THROW("Expected '%s' but found '%s' - not strictly compatible ('%s' arg %d)", expected, found, pToken->GetString(), i);
								break;
							case MATCH_RESTRICTION_BASIC:
								if(!bBasicMatch)
									THROW("Expected '%s' but found '%s' - not basically compatible ('%s' arg %d)", expected, found, pToken->GetString(), i);
								break;
							}
							if(!bBasicMatch) THROW("Expected '%s' but found '%s' ('%s' arg %d)", expected, found, pToken->GetString(), i);
						}
					}
					else nParamCompatibilityScore = 3;		// this arg is ambiguous and could be perfect for all we know...
				}

				// Translate this token for the compiler
				if(bFoundBestOverload) TranslateToken(pToken);

				// If we've got a totally failing param (we shouldn't actually be here), skip this command
				if(nParamCompatibilityScore)
					nCompatibilityScore += nParamCompatibilityScore;
				else
				{
					if(!pLink->IsReturn()) nCompatibilityScore = 0;
					break;
				}

				if(bVarArgMode) --nVarArgLimit;
				else ++pLink;
				++i;
			}
		}

		// Have we beat the record for most compatible command?
		if(nCompatibilityScore > nBestCompatibilityScore)
		{
			nBestCompatibilityScore = nCompatibilityScore;
			pMostCompatibleCommand = pCommand;
		}

		// Get the next overload or end the search
		if(!(pCommand = pCommand->GetNextOverload()))
		{
			if(!bFoundBestOverload)
			{
				pCommand = pMostCompatibleCommand;
				bFoundBestOverload = true;
			}
			else break;
		}
		else if(bFoundBestOverload) break;
	}

	// Update the command
	pCommand = pMostCompatibleCommand;
	pCommandToken->SetValue(pCommand);

	/*if(pCommand->GetParseFunc() == -1)
	{
		// This construct may be for the next line
		for(int i=nFirstTokenID; (pToken = RecallToken(i)) && pToken->GetType() == TOKEN_CONSTRUCT; i++)
			ParseConstruct(pToken, pCommandToken);
	}*/
#endif
}
void CParser::ProcessCommandArg(CToken *pToken, const CCommandArg * pArg)
{
	if(pArg->IsLabel())
	{
		if(pToken->GetType() == TOKEN_STRING)
		{
			// If this is a label pointer for a label that doesn't exist yet, just 'declare' it
			if(s_pLabel = CLabels::Add(s_pCurrentToken, s_nNumCodeLines))
			{
				// Index it so we can actually find it by name
				CLabels::IndexName(s_pLabel);

				//if(s_pScript->GetLabel(s_pLabel->GetHash()))
				//	THROW("Label '%s' already exists", s_pLabel->GetName());

				s_pScript->Add(s_pLabel);
				s_pLabel->WaitingForDefinition(true);
			}
		
			pToken->SetValueType(pArg->GetType()->Standardise());
			pToken->SetType(TOKEN_LABEL);
			pToken->SetValue(s_pLabel);
		}
	}
}
bool CParser::ParseCommandToken(CToken *pCommandToken, CToken *pToken, const CType * ExpectedType, int TokenIdx)
{
	// We might need to parse a special command token.. (that's what this function is for)
	auto pCommand = pCommandToken->GetCommandValue();
	s_nLastParseID = pCommand->GetParseFunc();

	switch(s_nLastParseID)
	{
	case SCRIPT_IF:
		s_pLogicToken = pCommandToken;

		// The number of conditions will be increased in ProcessEndOfConditionList...
		pToken->SetValueType(ExpectedType);
		pToken->SetValue(0);
		s_nNumConditions = 0;
		s_bLogicalOperator = -1;

		if(pToken = RecallToken(TokenIdx++))
		{
			if(pToken->GetType() == TOKEN_CONSTRUCT)
			{
				// Check for invalid placements
				switch(pToken->GetConstruct())
				{
				case SCR_NOT: break;
				default:
					throw(SCRambl.GetText("ERRORS", "E1107"));
				}
				pToken = RecallToken(TokenIdx++);
			}

			// First token of the first IF condition...
			switch(pToken->GetType())
			{
			case TOKEN_COMMAND:
				{
					auto pConditionCmd = pToken->GetCommandValue();
			
					if(!pConditionCmd->IsConditional())
						throw(SCRambl.GetText("ERRORS", "E1111"));

					//(*s_nNumConditions)++;
					ParseCommand(pToken, pConditionCmd, TokenIdx);
					break;
				}
			case TOKEN_VARINDEX:
			case TOKEN_VAR:
			case TOKEN_VALUE:
				ParseMath(TokenIdx-1);
				break;
			default: throw(SCRambl.GetText("ERRORS", "E1107"));
			}
		}
		
		break;
	case SCRIPT_NAME:
		if(pToken->GetType() == TOKEN_STRING)
			s_pSubScript->GetParseInfo()->SetName(pToken->GetString());
		s_nLastParseID = -1;
		return false;
	case SCRIPT_GOTO:
		{
			if(!s_pLabel) s_pLabel = CLabels::GetByName(pToken->GetString());
			if(s_pLabel)
			{
				pToken->SetValue(s_pLabel);
				pToken->SetType(TOKEN_LABEL);
			}
		}
		break;
	default:
		s_nLastParseID = -1;
		return false;
	}
	return true;
}
void CParser::TranslateToken(CToken *pToken)
{
	auto pType = pToken->GetArgType();
	bool bPromotedBySubScript = false;
	if(auto pSubToken = pToken->GetSubToken())
	{
		if(!pType->IsAnyArray())
		{
			int type = pType->GetType();
			type |= PT_ARRAY;
			int nIdx = CSyntax::GetBasicTypeIndex((PType)type);
			if(nIdx != TYPE_NONE)
			{
				pType = CSyntax::GetType(nIdx);
				bPromotedBySubScript = true;
			}
		}
	}
	auto nValSize = pToken->GetValueSize();
	auto &pTransLines = pType->GetNumTranslations() ?  pType->GetTranslations() : pType->Standardise()->GetTranslations();
	const CTranslation * pTranslation = nullptr;
	int nMatchCondSize = 0;

	// Get the right translation line for the argument
	for(auto pLink = pTransLines.Begin(); pLink != pTransLines.Bad(); ++pLink)
	{
		bool	bMatch = true,
				bExactMatch = true;

		// If the translation is for an array, check we've got the right type.
		if(pLink->IsArray())
		{
			// If anything doesn't add up, skip straight to the next translation
			auto pSubToken = pToken->GetSubToken();
			if(!pSubToken || !pSubToken->GetArgType()) continue;
			auto pValType = pSubToken->GetArgType();
			if(pValType != pLink->GetArrayIndexType()) continue;
		}
		else if(pToken->GetType() == TOKEN_VAR && pToken->GetSubToken()) continue;

		int		nMatchSize = 0;

		// If any is a false, we speedily move along
		for(int i=0,l=pLink->GetNumConditions(); i<l; ++i)
		{
			eTranslateCondition eCond;
			if(!(eCond = pLink->GetCondition(i))) break;
			auto nCondVal = pLink->GetConditionValue(i);
			if(eCond != TRANSLATE_COND_STRLEN_STR)
			{
				switch(pLink->GetDataType(i))
				{
				case DT_INT32:
				case DT_FLOAT32:
					nCondVal = 4;
					break;
				case DT_INT24:
				case DT_FLOAT24:
					nCondVal = 3;
					break;
				case DT_INT16:
				case DT_FLOAT16:
				case DT_FIXED16:
					nCondVal = 2;
					break;
				case DT_INT8:
				case DT_FLOAT8:
					nCondVal = 1;
					break;
				case DT_NULL:
					nCondVal = 0;
					break;
				}
			}
			switch(eCond)
			{
			case TRANSLATE_COND_SIZEOF_ARRAY:
				if(pToken->GetType() != TOKEN_VAR) break;

				// Use this translation line if the var array size fits the limit
				nMatchSize += nCondVal;
				if(GetIntNumBytes(pToken->GetVariableValue()->GetArraySize()) > nCondVal)
				{
					bMatch = false;
					continue;
				}
				if(bExactMatch) bExactMatch = pToken->GetVariableValue()->GetArraySize() == nCondVal;
				continue;
			case TRANSLATE_COND_SIZEOF_VAL:
				if(!pType->IsAnyVal()) break;
			case TRANSLATE_COND_SIZEOF_VAR:
				if(eCond == TRANSLATE_COND_SIZEOF_VAR && !pType->IsAnyVar()) break;
			case TRANSLATE_COND_SIZEOF_INDEXVAR:
				if(eCond == TRANSLATE_COND_SIZEOF_INDEXVAR && !pType->IsAnyVar()) break;
			case TRANSLATE_COND_STRLEN_STR:
			default:
				// Use this translation line for most values + var indexes
				nMatchSize += nCondVal;
				if(eCond == TRANSLATE_COND_SIZEOF_INDEXVAR	? pToken->GetSubToken()->GetValueSize() > nCondVal
															: pToken->GetValueSize() > nCondVal)
				{
					bMatch = false;
					continue;
				}
				if(bExactMatch) bExactMatch = eCond == TRANSLATE_COND_SIZEOF_INDEXVAR	? pToken->GetSubToken()->GetValueSize() == nCondVal
																						: pToken->GetValueSize() == nCondVal;
				continue;
			}
			bMatch = false;
			bExactMatch = false;
			break;
		}

		// Got a match?
		if(bMatch && (!pTranslation || nMatchSize < nMatchCondSize || (!pTranslation->IsArray() && bPromotedBySubScript && pLink->IsSubScriptPromoted())))
		{
			pTranslation = *pLink;
			if(bExactMatch) break;
		}
	}

	if(!pTranslation)
		throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1112"), NULL, 0, "%s%d%s", pToken->GetString(), pToken->GetValueSize(), pType->GetName()));

	pToken->Translate(pTranslation);
}

const CEnumerator * CParser::GetEnumerator(CToken *pToken, CToken *pCmdToken, int nParam)
{
	// Check for an enum specific to this command first
	if(pCmdToken)
	{
		const CCommandArg * pArg;
	
		if(pCmdToken->GetType() == TOKEN_COMMAND)
			pArg = pCmdToken->GetCommandValue()->GetArg(nParam);
		else if(pCmdToken->GetType() == TOKEN_OPERATOR)
			pArg = nParam ? pCmdToken->GetOperationValue()->GetRightArg() : pCmdToken->GetOperationValue()->GetLeftArg();
		else
			return nullptr;

		if(auto pEnum = pArg->GetType()->IsExtended() ? pArg->GetType()->Extend()->GetEnum() : nullptr)
			return pEnum->GetEnumerator(pToken->GetString());
	}
	return nullptr;
}
const CConstant<int> * CParser::GetConstant(CToken *pToken, const CTypeBase * pType)
{
	const CConstant<int> * pConstant = nullptr;

	// Check for a constant
	if(pToken->GetValueType() == PT_NONE)
	{
		// Script-defined constants take precedence...
		if(!(pConstant = CConstants::Get(pToken->GetString())))
		{
			if(!(pConstant = CConstants::GetFromFile(pToken->GetString())))
				pConstant = CConstants::GetModel(pToken->GetString());
		}
	}
	return pConstant;
}
const CConstant<int> * CParser::GetConstant(const char *name)
{
	const CConstant<int> * pConstant = nullptr;

	// Script-defined constants take precedence...
	if(!(pConstant = CConstants::Get(name)))
	{
		if(!(pConstant = CConstants::GetFromFile(name)))
			pConstant = CConstants::GetModel(name);
	}
	return pConstant;
}
void CParser::SetTokenToConstant(CToken *pToken, const CConstant<int> *pConstant)
{
	/*if(pConstant->GetBasicType() & PT_ANYVAR)
	{
		pToken->SetType(TOKEN_VAR);
		pToken->SetValue(s_pScript->FindVariable(s_pCurrentToken));
	}
	else if(pConstant->GetBasicType() & PT_INT)
	{
		if(pConstant->IsLabel())
		{
			pToken->SetType(TOKEN_LABEL);
			pToken->SetValue(s_pScript->FindLabel(s_pCurrentToken));
		}
		else
		{
			pToken->SetType(TOKEN_VALUE);
			pToken->SetValue(pConstant->GetIntValue());
		}
	}
	else if(pConstant->GetBasicType() & PT_FLOAT)
	{
		pToken->SetType(TOKEN_VALUE);
		pToken->SetValue(pConstant->GetFloatValue());
	}
	else
	{
		pToken->SetString(pConstant->GetString());
	}

	pToken->SetValueType(pConstant->GetBasicType());*/
}

void CParser::ValidateStringToken(CToken *pToken, const CType * ExpectedType, CToken *pCmdToken, int nParam)
{
	// Ensure a string is actually a string and not an identifier
	const CConstant<int> * pConstant = nullptr;

	// Dont waste time checking for predefined constants for a return
	if(nParam != -1)
	{
		// Check for an enum specific to this command first
		if(!(pConstant = GetEnumerator(pToken, pCmdToken, nParam)))
			pConstant = GetConstant(pToken, ExpectedType);
	}
	else
	{
		// Perhaps we need a script-defined constant?
		pConstant = CConstants::Get(pToken->GetString());
	}

	// ... we add the string sizes here
	if(pConstant) SetTokenToConstant(pToken, pConstant);
	else
	{
		// Single char for an int param? Convert it to an integer...
		const char * pStr = pToken->GetString();
		if(ExpectedType->IsInt() && *pStr && pStr[1] == '\0')
		{
			pToken->SetValue(*pStr);
			pToken->SetValueType(PT_INT);
		}
		else ValidateString(pToken, ExpectedType);
	}
}
void CParser::ValidateString(CToken *pToken, const CType * Type)
{
	// Make sure the string fits into the argument
	size_t len = pToken->GetStringLength();

	if(Type->IsString())
	{
		if(len > CLimits::GetMaxStringLength())
			throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1113"), NULL, 0, "%d", CLimits::GetMaxStringLength()+1));
		//if(Type->GetSize() > 0 && len > Type.GetSize())
		//	THROW("STRING length exceeds the limit of %d characters set for this command", Type.GetSize());

		pToken->SetValue(len);
		pToken->SetValueType(PT_STRING);
	}
	else if(Type->IsTextLabel())
	{
		if(len > CLimits::GetMaxTextLabelLength())
			throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1114"), NULL, 0, "%d", CLimits::GetMaxTextLabelLength()+1));
		//if(Type.GetSize() > 0 && len > Type.GetSize())
		//	THROW("TEXT_LABEL length exceeds the limit of %d characters set for this command", Type.GetSize());

		pToken->SetValue(len);
		pToken->SetValueType(PT_TEXT_LABEL);
	}
	else
	{
		pToken->SetValueType(PT_STRING);
	}
}

void CParser::DoRegisterCommand()
{
	// Called after "#register_command"
	DWORD opcode;
	eTokenType ttype = GetTokenData();
	
	if(!sscanf(s_pCurrentToken, "%4x", &opcode))
	{
		if(ttype != TOKEN_VALUE) throw(SCRambl.GetText("ERRORS", "E1115"));
		opcode = StringToInt(s_pCurrentToken);
	}

	if(opcode && opcode < MAX_OPCODES)
	{
		ttype = GetTokenData();
		bool	bIsOverloaded = false,
				bIsCond = false;

		if(ttype != TOKEN_COMMAND)		// check for a command token in case we're 'overloading'
		{
			if(ttype != TOKEN_STRING)
				throw(SCRambl.GetText("ERRORS", "E1116"));
		}
		else bIsOverloaded = true;

		if(*s_pCurrentToken == '?')
		{
			bIsCond = true;
			++s_pCurrentToken;
		}

		if(!CCommands::CheckCommandName(s_pCurrentToken))
			throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1117"), NULL, 0, "%s", s_pCurrentToken));
							
		auto pCommand = CCommands::Add((short)opcode, s_pCurrentToken, true);
		pCommand->SetIsConditional(bIsCond);
							
		if(bIsOverloaded)
		{
			if(pCommand->DoesOverloadConflict(s_pCommand))
				Warning(SCRambl.GetText("WARNINGS", "1004"));

			if(auto pOvCmd = CCommands::Get((short)opcode))
			{
				pCommand->SetPrevOverload(pOvCmd);
				pOvCmd->SetNextOverload(pCommand);
			}
		}

		char * pStr = strtok(++s_pLineCurrent, ")");
		if(pStr) CCommands::ParseCommandArgList(pCommand, pStr);

		CCommands::AddToList(pCommand);
	}
}
void CParser::DoRegisterType()
{
	// Called after #register_type
	if(GetTokenData() != TOKEN_TYPENAME)
		throw(SCRambl.GetText("ERRORS", "E1118"));
	
	auto pType = CSyntax::GetTypeByName(s_pCurrentToken);
	bool bIsExtended = pType->IsExtended();

	auto ttype = GetTokenData();
	if(ttype == TOKEN_TYPENAME)
		throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1119"), NULL, 0, "%s", s_pCurrentToken));
	auto name = s_pCurrentToken;

	if(GetTokenData() != TOKEN_STRING)
		throw(SCRambl.GetText("ERRORS", "E1116"));
	
	auto id = s_pCurrentToken;
	
	if(!GetTokenData(TOKEN_STRING))
		throw(SCRambl.GetText("ERRORS", "E1116"));

	eTokenType typetok = TOKEN_NONE;
	if(!strcmp(s_pCurrentToken, "VAR")) typetok = TOKEN_VAR;
	else if(!strcmp(s_pCurrentToken, "VALUE")) typetok = TOKEN_VALUE;
	else if(!strcmp(s_pCurrentToken, "CONSTANT")) typetok = TOKEN_CONSTANT;
	else if(!strcmp(s_pCurrentToken, "LABEL")) typetok = TOKEN_LABEL;
	else if(!strcmp(s_pCurrentToken, "MODEL")) typetok = TOKEN_MODEL;
	else if(!strcmp(s_pCurrentToken, "STRING")) typetok = TOKEN_STRING;
	else throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1120"), NULL, 0, "%s", s_pCurrentToken));

	CSyntax::AddExtendedType(pType->Standardise()->GetInternalID(), id, name, typetok);
}
void CParser::DoRegisterVar()
{
	// Called after #register_var
	eTokenType ttype;

	if(GetTokenData() != TOKEN_VALUE)
		throw(SCRambl.GetText("ERRORS", "E1121"));
	if(!IsStringInt(s_pCurrentToken))
		throw(SCRambl.GetText("ERRORS", "E1122"));

	int nIndex = StringToInt(s_pCurrentToken);
	
	if(GetTokenData() != TOKEN_TYPENAME)
		throw(SCRambl.GetText("ERRORS", "E1118"));
	
	auto pType = CSyntax::GetTypeByName(s_pCurrentToken);
	if(!pType) throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1123"), NULL, 0, "%s", s_pCurrentToken));
	
	ttype = GetTokenData();
	if(ttype == TOKEN_VAR)
		throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1124"), NULL, 0, "%s%d", s_pVariable->GetName(), s_pVariable->GetDeclarationLine()));
	if(ttype != TOKEN_STRING)
		throw(SCRambl.GetText("ERRORS", "E1116"));

	const char * szName = s_pCurrentToken;
	int nArraySize = 0;
	bool bIsTimer = false;

	if(ttype = GetTokenData())
	{
		if(ttype != TOKEN_VALUE)
			throw(SCRambl.GetText("ERRORS", "E1125"));
		if(!IsStringInt(s_pCurrentToken))
			throw(SCRambl.GetText("ERRORS", "E1126"));
		
		nArraySize = StringToInt(s_pCurrentToken);

		if(ttype = GetTokenData())
		{
			bIsTimer = ttype == TOKEN_STRING ? !stricmp(s_pCurrentToken, "true") : StringToInt(s_pCurrentToken) != 0;
		}
	}

	auto pVar = CVariables::Add(szName, pType, nIndex, nArraySize, true);
	pVar->IsPredefined(true);
	pVar->IsTimer(bIsTimer);
	
	/*const char * szName = s_pCurrentToken;

	if(GetTokenData() != TOKEN_VALUE || !IsStringInt(s_pCurrentToken))
		throw("expected INT (var index)");
	
	int nIndex = StringToInt(s_pCurrentToken);

	GetTokenData();
	auto pType = CSyntax::GetTypeByID(s_pCurrentToken);
	if(!pType) THROW("type '%s' not found", s_pCurrentToken);

	if(GetTokenData() != TOKEN_VALUE || !IsStringInt(s_pCurrentToken))
		throw("expected INT (array size)");
	int nArraySize = StringToInt(s_pCurrentToken);

	ttype = GetTokenData();

	if(!IsStringInt(szOffset)) THROW("Invalid offset '%s'", szOffset);
	if(!IsStringInt(szArraySize)) THROW("Invalid array size '%s'", szArraySize);

	bool bIsTimer;
	if(IsStringInt(szIsTimer)) bIsTimer = StringToInt(szIsTimer) != 0;
	else bIsTimer = !strnicmp(szIsTimer, "true", 4);
				
	int offset = StringToInt(szOffset);
	int array_size = StringToInt(szArraySize);
	auto pType = CSyntax::GetTypeByID(szType);

	if(!pType) THROW("Type '%s' not found", szType);

	auto pVar = CVariables::Add(szName, pType, offset, array_size, true);
	pVar->IsPredefined(true);
	pVar->IsTimer(bIsTimer);*/
}
void CParser::DoIncludeFile()
{
	// Get the path
	if(GetTokenData() != TOKEN_STRING)
		throw(SCRambl.GetText("ERRORS", "E1128"));
						
	// Localize the path
	char szIncPathStr[MAX_PATH];
	auto szIncPath = SCRambl.GetFullPath(s_pCurrentToken, szIncPathStr);

	BuildEvents[INCLUDE_FILE][BEFORE]();

	// Cheapest file include system ever?
	auto pScriptFile = szIncPath ? new CScriptFile(szIncPath, FILE_MODE_READ, FILE_TYPE_TEXT) : nullptr;
	
	if(!pScriptFile || !pScriptFile->OK())
		throw(SCRambl.BuildText(SCRambl.GetText("GENERAL", "OERR"), NULL, 0, "%s", szIncPath));
		
	int nNumLines = s_nNumCodeLines;
	try
	{
		auto pCurrentScriptFile = s_pScriptFile;
		Parse(pScriptFile);
		s_pScriptFile = pCurrentScriptFile;
		s_nNumCodeLines = nNumLines;
	}
	catch(const char *e)
	{
		// Error encountered?
		s_bErrorHandled = true;			// tell the one catching all this not to try and print out the line number,
										// which will simply be the sum of the included and source files line numbers, lol
		throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1130"), NULL, 0, "%s%s%d%d", e, szIncPath, pScriptFile->GetLineNumber(), nNumLines));
	}

	delete pScriptFile;
	BuildEvents[INCLUDE_FILE][AFTER]();
}
void CParser::DoImportFile()
{
	// Get the mode
	if(GetTokenData() != TOKEN_STRING)
		throw(SCRambl.GetText("ERRORS", "E1142"));
	
	auto szMode = s_pCurrentToken;

	// Get the path
	if(GetTokenData() != TOKEN_STRING)
		throw(SCRambl.GetText("ERRORS", "E1141"));

	// Search for the file path...
	auto szIncPath = SCRambl.GetFullPath(s_pCurrentToken);

	// Do the importy bit
	auto pFile = szIncPath ? new CFile(szIncPath, FILE_MODE_READ) : nullptr;
	if(pFile && pFile->OK())
	{
		CToken * pToken;
		if(!stricmp(szMode, "bin"))
		{
			pToken = RegisterToken(TOKEN_BIN_FILE, szIncPath);
			pToken->SetValue((int)pFile->GetSize());
		}
		else if(!stricmp(szMode, "commands"))
		{
			CCommands::LoadFromFile(szIncPath);
		}
		else if(!stricmp(szMode, "operators"))
		{
			COperators::LoadFromFile(szIncPath);
		}
		else if(!stricmp(szMode, "constants"))
		{
			CConstants::LoadFromFile(szIncPath);
		}
		else if(!stricmp(szMode, "types"))
		{
			CSyntax::LoadTypes(szIncPath);
		}
		else throw(SCRambl.BuildText(SCRambl.GetText("ERRORS", "E1143"), NULL, 0, "%s", szMode));
		delete pFile;
	}
	else throw(SCRambl.BuildText(SCRambl.GetText("GENERAL", "OERR"), NULL, 0, "%s", szIncPath));
}
void CParser::DoFunctionBegin()
{
	auto pCommand = GetParseCommand(SCRIPT_GOTO);
	auto pToken = RegisterToken(TOKEN_COMMAND, pCommand->GetName());
	pToken->SetValue(pCommand);
	sprintf(g_szTempString, "__SPECIAL_LABELS_FUNCTION_%d", s_nNumFunctions++);
	CreateInternalLabel(g_szTempString);
	s_pFunctionEndLabel = s_pLabel;
	pToken = RegisterToken(TOKEN_LABEL, g_szTempString);
	pToken->SetValue(s_pFunctionEndLabel);
	pToken->SetValueType(CSyntax::GetExtendedType(ET_LABEL));
	TranslateToken(pToken);

	if(auto pToken = ProcessLabel(true, true))
	{
		s_pFunctionLabel = s_pLabel;
		s_pLabel->IsFunction(true);
	}
	else throw(SCRambl.GetText("ERRORS", "E1116"));
}
void CParser::DoFunctionEnd()
{
	auto pToken = RegisterToken(TOKEN_LABEL, s_pFunctionEndLabel->GetName());
	pToken->SetValue(s_pFunctionEndLabel);
	pToken->IsSkippedDuringCompilation(true);
	NextLine();
}